Skip to content

fix(MongoDb): Probe the server instead of counting log messages before initiating - #1735

Open
arnelirobles wants to merge 1 commit into
testcontainers:developfrom
arnelirobles:bugfix/1732-replica-set-readiness-probe
Open

fix(MongoDb): Probe the server instead of counting log messages before initiating#1735
arnelirobles wants to merge 1 commit into
testcontainers:developfrom
arnelirobles:bugfix/1732-replica-set-readiness-probe

Conversation

@arnelirobles

@arnelirobles arnelirobles commented Aug 9, 2026

Copy link
Copy Markdown

Fixes #1732

The problem

The readiness check that runs before the replica set is initiated counts occurrences of a log message and compares that count for equality:

var (stdout, stderr) = await container.GetLogsAsync(since: container.StoppedTime, timestampsEnabled: false);
return _count.Equals(... .Count(line => line.Contains("Waiting for connections")));

_count is 1 or 2. The count only grows, and the module does not control what else writes that text, so the check fails in both directions:

  • Too many occurrences and the start hangs. The equality can never become true. The default wait strategy timeout is one hour, so the caller does not fail, it appears to hang.
  • A stale occurrence and the start returns early. The check passes before mongod is serving, and the next command fails with MongoNetworkError: connect ECONNREFUSED 127.0.0.1:27017.

@xamir82's reproduction on the issue shows this needs no restart: extra markers written by an init script during a single run are enough, and whether it fails at all depends on when the once-per-second poll samples.

This arrived in 4.11.0. #1656 (ec6b50f6) put this check on the replica set path; before that the path did not use it.

The fix

Ask the server instead of reading its logs:

private const string ScriptContent = "try{rs.status();quit(0);}catch(e){quit(e.codeName===\"NotYetInitialized\"?0:1);}";

This still satisfies what #1656 needed for #1636. The official image forks a temporary mongod during first-time initialization to create the root user, and that process is not started with --replSet, so it never reports NotYetInitialized. Only the final server does.

Measured on mongo:6.0.27 with the module's own configuration (auth plus the generated keyfile), polling twice a second from container start:

t authenticated ping rs.status() "Waiting for connections"
1.0s fails no reply 1
2.0s succeeds NotYetInitialized 2

A plain ping is not sufficient on its own; against a container without the keyfile it succeeds during the bootstrap phase. rs.status() is what distinguishes the two processes.

It is also idempotent for an already initiated set, which returns ok: 1 and exits zero.

Scope

Limited to the replica set path, which is where #1656 introduced this. Build() still uses the log-counting strategy for non-replica-set containers, and it has the same weakness, but changing it alters long-standing behaviour for every MongoDb container and seemed better decided separately. Happy to extend this PR if you would prefer both in one go.

Testing

MongoDbReplicaSetReadinessTest follows @xamir82's reproduction: a replica set container with an init script that writes the marker twice more.

  • Before: times out (bounded to three minutes in the test so a regression fails rather than stalls the run).
  • After: passes in about seven seconds.
  • Full Testcontainers.MongoDb.Tests: 19 passed, 0 failed, including every replica set fixture, which is what covers the first-time initialization path from [Bug]: MongoDB stops while starting #1636.

Separate from #1731, which is about rs.initiate not being idempotent. The two touch the same method but neither depends on the other.

Summary by CodeRabbit

  • Bug Fixes

    • Improved MongoDB replica-set startup detection by verifying replication status directly.
    • Containers now start reliably even when readiness messages appear multiple times.
    • Replica sets in the intermediate NotYetInitialized state are handled correctly.
  • Tests

    • Added regression coverage for duplicate readiness messages and successful replica-set initialization.

…e initiating

The readiness check that runs before the replica set is initiated counts how
often "Waiting for connections" appears in the log and compares that count for
equality. The count only grows and the module does not control what else writes
that text, so any additional occurrence pushes it past the expected value and
the check can never match. The default wait strategy timeout is one hour, so the
container start appears to hang. A stale occurrence can also satisfy the check
before mongod is serving, which surfaces as ECONNREFUSED on the next command.

Ask the server instead. rs.status() answers only once the final mongod is
serving: the temporary mongod the official image forks during first-time
initialization is not started with --replSet, so it never reports
NotYetInitialized. That keeps the handover guarantee testcontainers#1656 added for testcontainers#1636
while removing the dependency on log content.

Scoped to the replica set path, which is where testcontainers#1656 introduced this. The
non-replica-set wait strategy still counts log messages and has the same
weakness, but changing it affects long-standing behaviour for every MongoDb
container and is better decided separately.
@netlify

netlify Bot commented Aug 9, 2026

Copy link
Copy Markdown

Deploy Preview for testcontainers-dotnet ready!

Name Link
🔨 Latest commit 7c29b2b
🔍 Latest deploy log https://app.netlify.com/projects/testcontainers-dotnet/deploys/6a77daf0e6a6ce000786f9f3
😎 Deploy Preview https://deploy-preview-1735--testcontainers-dotnet.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai

coderabbitai Bot commented Aug 9, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: a4cf7d9b-7bb4-479f-856d-7ba9decf5da8

📥 Commits

Reviewing files that changed from the base of the PR and between 1d329ad and 7c29b2b.

📒 Files selected for processing (2)
  • src/Testcontainers.MongoDb/MongoDbBuilder.cs
  • tests/Testcontainers.MongoDb.Tests/MongoDbReplicaSetReadinessTest.cs

Walkthrough

MongoDB replica-set startup now waits for rs.status() to complete successfully instead of counting readiness log messages. A regression test covers duplicate readiness messages and verifies replica status after startup.

Changes

MongoDB replica-set readiness

Layer / File(s) Summary
Replica-status readiness
src/Testcontainers.MongoDb/MongoDbBuilder.cs
Replica-set initialization uses WaitReplicationEnabled. The strategy executes rs.status() and treats successful completion, including NotYetInitialized, as readiness.
Readiness regression coverage
tests/Testcontainers.MongoDb.Tests/MongoDbReplicaSetReadinessTest.cs
The test emits duplicate readiness messages, starts the container with a three-minute timeout, and verifies that rs.status().ok executes successfully.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

Suggested labels: bug

Suggested reviewers: hofmeisteran

Poem

A rabbit checks the replica set,
No log-count traps remain to fret.
rs.status() hops into view,
Duplicate messages cannot fool.
Startup bounds keep time in sight.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The change fixes replica-set readiness but leaves the non-replica-set log-counting defect from issue #1732 unresolved. Extend the fix to all affected MongoDB readiness paths, or narrow and split issue #1732 so the remaining non-replica-set behavior is tracked separately.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes replacing log counting with server probing before replica-set initiation.
Description check ✅ Passed The description explains the problem, fix, scope, related issue, and test coverage in sufficient detail.
Out of Scope Changes check ✅ Passed The implementation and regression test directly support the replica-set readiness objective and introduce no unrelated changes.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Warning

⚠️ This pull request shows signs of AI-generated slop (redundant_comments). It has been flagged by CodeRabbit slop detection and should be reviewed carefully.

@arnelirobles

Copy link
Copy Markdown
Author

One more argument for this direction that I missed when opening the PR: testcontainers-go already gates replica set readiness on rs.status() rather than on log output.

// modules/mongodb/mongodb.go
req.WaitingFor = wait.ForAll(
    req.WaitingFor,
    wait.ForExec(cli.eval("rs.status().ok")),
).WithDeadline(60 * time.Second)

https://github.com/testcontainers/testcontainers-go/blob/main/modules/mongodb/mongodb.go#L207

So this is not a new idea, it brings the .NET module in line with how the Go one already handles the same problem. Same command, and the same reason: rs.status() answers only once the final mongod is serving, and it does not depend on log content.

@HofmeisterAn HofmeisterAn added module An official Testcontainers module bug Something isn't working labels Aug 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working module An official Testcontainers module

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: MongoDb WaitIndicateReadiness counts log lines from the previous run, so a restarted container either hangs or is reported ready too early

2 participants