#[SOLVED] Wait until Postgres is ready to handle connections (healthcheck?)

1 messages · Page 1 of 1 (latest)

fluid pasture
#

I'm creating a PHP container with two associated services; Redis and Postgres.

Before going too far in my CI, I would like to capture as quick as possible connectivity errors: can PHP connect to Redis and can PHP connect to Postgres? And to stop my CI in case of problems.

I would like to wait until both Redis and Postgres are "ready" before continue my Python code. Like I do using depends_on in a compose.yaml file.

For Redis, my code works (at the bottom of my message below); not for Postgres. Postgres is still starting up and the database not yet ready.

More detail below.


I've a code like this:

self.container = (
    # So attach the postgres container to my PHP one
    await self.container.with_service_binding(postgres.host, postgres.server)
    # [...]
)

# Test if the connection if successfull i.e. port, host, user, password, ... are OK and connectivity works
self.container = await self.container.with_exec(
    ["psql", "-h", postgres.host, "-p", postgres.port, "-U", postgres.username],
    expect=ReturnType.ANY,
).sync()

output: str = await self.container.stdout()

# For the illustration, I run an exception just to see what stdout() contains
raise RuntimeError(    
    f"{await self.container.stdout()}\n"
)

And I get this output:
psql: error: connection to server at "(my host)" (10.87.1.115), port 5432 failed: FATAL: the database system is starting up

So... ouch... Postgres isn't yet ready. ==> how can I wait for it's ready?

If I do the same for Redis; it works:

self.container = (
    await self.container.with_exec(["pecl", "install", "redis"])
    .with_exec(["docker-php-ext-enable", "redis"])
    # [...]
)

# [...]

self.container = await self.container.with_exec(
    ["redis-cli", "-h", redis.host, "--pass", redis.password, "ping"],
    expect=ReturnType.ANY,
).sync()

output: str = str(await self.container.stdout()).strip().upper()

# And output is well "PONG"; nice!

Thanks!

#

Wait until Postgres is ready to handle connections (healthcheck?)

shy owl
fluid pasture
shy owl
shy owl
fluid pasture
fluid pasture
#

If this post can be useful to someone else, here is how I've implement the suggested approach by code :

async def healthcheck(
    self, ctr: Container, timeout: int = 120
) -> None:
    """Make sure the Postgres container is ready. For this, we'll use pg_isready.

    Args:
        ctr (Container): An instance to the Postgres container
        timeout (int, optional): Maximum time we'll wait. Defaults to 120.

    Raises:
        EnvironmentError: There is an unexpected error
        EnvironmentError: Timeout reached; the postgres server is not ready
    """
    command: str = (
        f"pg_isready --host {self.host} --port {self.port} --dbname {self.dbname} -U {self.username}"
    )

    start_time: float = time.time()

    while time.time() - start_time < timeout:
        try:
            ctr = await ctr.with_exec(command.split(), expect=ReturnType.ANY).sync()

            stdout: str = await ctr.stdout()
            exit_code: int = int(await ctr.exit_code())

            Console(f"INFO - Postgres {exit_code}: {stdout}")

            if exit_code == 0:
                return  # PostgreSQL is ready!

            time.sleep(1)

        except Exception as exception:
            raise EnvironmentError(
                f"An unexpected error occurred: {exception}"
            )

    raise EnvironmentError(
        f"Timeout {timeout} reached. Output of pg_isready: {stdout}."
    )

This method is part of a Python class where I've defined attributes like the host, the port, ...

#

[SOLVED] Wait until Postgres is ready to handle connections (healthcheck?)