Core Concepts: The Start and Stop Lifecycle

Understanding what happens when you call Start() and Stop() is key to using embedded-postgres effectively, especially for managing data and running tests in parallel.

The Start() Lifecycle

When postgres.Start() is called, it performs a sequence of operations to ensure a clean, isolated PostgreSQL instance is ready for use.

  1. Port Availability Check: It first checks if the configured port (e.g., 5432) is available. If another process is listening on that port, Start() will fail immediately.

  2. Path Management: The library uses several key directories:

    • CachePath: Where downloaded binary archives (.txz files) are stored. Defaults to ~/.embedded-postgres-go. This path is persistent across runs.
    • RuntimePath: A temporary directory for the extracted binaries and other runtime files. By default, this directory is deleted and recreated on every Start() call to ensure a clean slate.
    • DataPath: The PostgreSQL data directory (PGDATA). By default, it's a subdirectory of RuntimePath, meaning it is also deleted on every start. To persist data between runs, you must configure DataPath to a location outside of RuntimePath.
  3. Binary Acquisition: It ensures the PostgreSQL binaries are available. This is covered in detail in the Binary Management guide.

  4. Database Initialization (initdb): It checks if the DataPath is a valid, initialized PostgreSQL data directory.

    • If DataPath is empty or contains data for an incompatible PostgreSQL version, it is wiped, and the initdb command is run to create a new database cluster.
    • If DataPath contains a valid, version-compatible cluster, it is reused. This is how data persistence is achieved.
  5. Start PostgreSQL Process: It executes pg_ctl start, pointing it to the prepared DataPath and passing any custom StartParameters.

  6. Create Initial Database: If the configured database name is not the default postgres, it connects to the server and runs CREATE DATABASE ....

  7. Health Check: It repeatedly tries to connect to the new PostgreSQL instance. Start() only returns successfully once a connection is established and a simple query (SELECT 1) succeeds. If it cannot connect within the StartTimeout period, it returns an error.

The Stop() Lifecycle

Calling postgres.Stop() performs a graceful shutdown of the PostgreSQL process.

  1. Execute pg_ctl stop: It runs the pg_ctl stop -w command, which waits for the server to shut down cleanly.
  2. Process Cleanup: This ensures no orphaned PostgreSQL processes are left running after your application or test suite finishes.

It is crucial to call Stop() to release resources and ensure a clean exit. A common pattern is to use defer:

postgres := embeddedpostgres.NewDatabase()
if err := postgres.Start(); err != nil {
    // handle error
}
defer postgres.Stop()

// ... your test or application logic