Skip to content

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

Description

@arnelirobles

Testcontainers version

4.11.0 (develop, 1d329ad1)

Using the latest Testcontainers version?

Yes

Host OS

macOS

Host arch

arm64

.NET version

10

Docker version

Docker Desktop, engine 28.x

What happened?

WaitIndicateReadiness in MongoDbBuilder is not safe across a container restart, in both directions.

// src/Testcontainers.MongoDb/MongoDbBuilder.cs
public async Task<bool> UntilAsync(IContainer container)
{
    var (stdout, stderr) = await container.GetLogsAsync(since: container.StoppedTime, timestampsEnabled: false)
        .ConfigureAwait(false);

    return _count.Equals(Array.Empty<string>()
        .Concat(stdout.Split(LineEndings, StringSplitOptions.RemoveEmptyEntries))
        .Concat(stderr.Split(LineEndings, StringSplitOptions.RemoveEmptyEntries))
        .Count(line => line.Contains("Waiting for connections")));
}

_count is 1 or 2 depending on whether credentials are configured, and the comparison is exact equality over the logs since StoppedTime. After a restart that window covers the previous run as well as the new one, so:

  • Too many matches: the start hangs. The count exceeds _count and the equality can never become true. For a replica set container this surfaces inside InitiateReplicaSetAsync, which awaits this readiness check before initiating. The default wait strategy timeout is one hour, so the caller appears to hang rather than fail.

  • Too few matches, at the wrong time: the start returns early. A single stale Waiting for connections line from the previous run can satisfy the check before the new mongod is listening. The next command then fails with MongoNetworkError: connect ECONNREFUSED 127.0.0.1:27017.

Steps to reproduce

Add a restart test to MongoDbContainerTest:

[Fact]
public async Task RestartsSuccessfully()
{
    using var cts = new CancellationTokenSource(TimeSpan.FromMinutes(2));

    await _mongoDbContainer.StopAsync(cts.Token);
    await _mongoDbContainer.StartAsync(cts.Token);

    var execResult = await _mongoDbContainer.ExecScriptAsync("db.adminCommand({ping:1}).ok;", cts.Token);
    Assert.True(0L.Equals(execResult.ExitCode), execResult.Stderr);
}

Observed across the existing fixtures:

Fixture Result
MongoDbNoAuthConfiguration fails in ~3s, MongoNetworkError: connect ECONNREFUSED 127.0.0.1:27017
MongoDbReplicaSetDefaultConfiguration hangs until the bound, TimeoutException from WaitStrategy.WaitUntilAsync via InitiateReplicaSetAsync
MongoDbNamedReplicaSetConfiguration same as above
MongoDbV5Configuration same as above
MongoDbDefaultConfiguration, MongoDbV4Configuration pass

Expected result / actual result

Expected: a stopped container that is started again becomes ready, and the readiness check reflects the current run.

Actual: the readiness check counts log lines from the previous run, so a restart either hangs until the wait strategy times out or is reported ready before mongod is accepting connections.

Additional information

Two things worth deciding together:

  1. Comparing with >= instead of exact equality would fix the hang but not the early return, since a stale line still counts.
  2. Scoping the log window to the current run (rather than StoppedTime, which refers to the previous stop) addresses both, but I am not sure what the intended semantics of StoppedTime are here.

Found while working on #1722; deliberately kept out of that pull request since it is a separate defect. Happy to open a PR once the preferred direction is clear.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions