Skip to content

Commit

Permalink
chore: Use the pg_isready command to assess whether PostgreSQL is r…
Browse files Browse the repository at this point in the history
…eady or not
  • Loading branch information
0xced authored Feb 11, 2024
1 parent 28df26e commit f9afd0c
Showing 1 changed file with 31 additions and 10 deletions.
41 changes: 31 additions & 10 deletions src/Testcontainers.PostgreSql/PostgreSqlBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,11 @@ public PostgreSqlBuilder WithPassword(string password)
public override PostgreSqlContainer Build()
{
Validate();
return new PostgreSqlContainer(DockerResourceConfiguration, TestcontainersSettings.Logger);

// By default, the base builder waits until the container is running. However, for PostgreSql, a more advanced waiting strategy is necessary that requires access to the configured database and username.
// If the user does not provide a custom waiting strategy, append the default PostgreSql waiting strategy.
var postgreSqlBuilder = DockerResourceConfiguration.WaitStrategies.Count() > 1 ? this : WithWaitStrategy(Wait.ForUnixContainer().AddCustomWaitStrategy(new WaitUntil(DockerResourceConfiguration)));
return new PostgreSqlContainer(postgreSqlBuilder.DockerResourceConfiguration, TestcontainersSettings.Logger);
}

/// <inheritdoc />
Expand All @@ -88,8 +92,7 @@ protected override PostgreSqlBuilder Init()
// Disable durability: https://www.postgresql.org/docs/current/non-durability.html.
.WithCommand("-c", "fsync=off")
.WithCommand("-c", "full_page_writes=off")
.WithCommand("-c", "synchronous_commit=off")
.WithWaitStrategy(Wait.ForUnixContainer().AddCustomWaitStrategy(new WaitUntil()));
.WithCommand("-c", "synchronous_commit=off");
}

/// <inheritdoc />
Expand Down Expand Up @@ -123,19 +126,37 @@ protected override PostgreSqlBuilder Merge(PostgreSqlConfiguration oldValue, Pos
/// <inheritdoc cref="IWaitUntil" />
private sealed class WaitUntil : IWaitUntil
{
private const string IPv4Listening = "listening on IPv4";

private const string IPv6Listening = "listening on IPv6";
private readonly string[] _command;

private const string DatabaseSystemReady = "database system is ready to accept connections";
/// <summary>
/// Initializes a new instance of the <see cref="WaitUntil" /> class.
/// </summary>
/// <param name="configuration">The container configuration.</param>
public WaitUntil(PostgreSqlConfiguration configuration)
{
_command = new[] {
"pg_isready",
"--host", "localhost", // Explicitly specify localhost in order to be ready only after the initdb scripts have run and the server is listening over TCP/IP
"--dbname", configuration.Database,
"--username", configuration.Username,
};
}

/// <inheritdoc />
/// <summary>
/// Test whether the database is ready to accept connections or not with the <a href="https://www.postgresql.org/docs/current/app-pg-isready.html">pg_isready</a> command.
/// </summary>
/// <returns><see langword="true"/> if the database is ready to accept connections; <see langword="false"/> if the database is not yet ready.</returns>
public async Task<bool> UntilAsync(IContainer container)
{
var (_, stderr) = await container.GetLogsAsync(since: container.StoppedTime, timestampsEnabled: false)
var execResult = await container.ExecAsync(_command)
.ConfigureAwait(false);

return new[] { IPv4Listening, IPv6Listening, DatabaseSystemReady }.All(stderr.Contains);
if (execResult.Stderr.Contains("pg_isready was not found"))
{
throw new NotSupportedException($"The {container.Image.FullName} image is not supported. Please use postgres:9.3 onwards.");
}

return 0L.Equals(execResult.ExitCode);
}
}
}

0 comments on commit f9afd0c

Please sign in to comment.