Add replication capabilities - #11
Conversation
30fd540 to
b89654b
Compare
checks for EVNs: - POSTGRES_REPLICATION_USER - POSTGRES_REPLICATION_PASSWORD - POSTGRES_PRIMARY_HOST
b89654b to
23d96f0
Compare
|
Example #1, keeping reference of AI prompts:
Notes:
|
|
Example #2:
AI forgot to mark comments as resolved:
NOTE: GitHub marks them as "Outdated" when the code/line changes not as resolved! |
|
Example #3 - same review asking Kimi K3 (performed after the 2nd commit which introduces
resulting in the review immediately below |
atodorov
left a comment
There was a problem hiding this comment.
Left a few inline comments. Overall the approach works, but there are a couple of correctness concerns worth addressing before this merges: (1) the replication slot name is derived from POSTGRES_REPLICATION_USER, which will collide if two replicas use the same user; (2) pg_basebackup runs before docker-entrypoint.sh gets a chance to configure the instance, so the replica inherits whatever defaults pg_basebackup writes but the initdb.d scripts never run on the replica's data dir; (3) the test hard-codes two sleep 120 waits which will make CI slow and flaky. Details inline.
| pg_basebackup \ | ||
| --dbname "postgres://$POSTGRES_REPLICATION_USER:$POSTGRES_REPLICATION_PASSWORD@$POSTGRES_PRIMARY_HOST/postgres?sslmode=require" \ | ||
| --pgdata "$PGDATA" \ | ||
| --progress --verbose --write-recovery-conf --wal-method stream \ |
There was a problem hiding this comment.
Using $POSTGRES_REPLICATION_USER as the replication slot name means the slot name is the same for every replica that connects with this user. If you ever spin up a second replica of postgres_17 (or tear one down and bring up a new one while the old slot still exists on the primary), pg_basebackup --create-slot will fail with "replication slot already exists", and worse, the old slot will keep retaining WAL on the primary even after the replica is gone. Consider using a dedicated slot-name variable (e.g. POSTGRES_REPLICATION_SLOT) defaulting to something unique per replica (hostname or container name), and document that operators must drop stale slots on the primary.
| echo "INFO: check if replication is configured" | ||
| if [ ! -s "$PGDATA/PG_VERSION" ] && [ -n "$POSTGRES_REPLICATION_USER" ]; then | ||
| mkdir -p "$PGDATA" | ||
| chmod 00700 "$PGDATA" || : |
There was a problem hiding this comment.
Because this block runs before docker-entrypoint.sh, pg_basebackup populates $PGDATA directly and the standard entrypoint's initdb logic will see an existing PG_VERSION and skip docker-entrypoint-initdb.d entirely. That's probably what you want for a replica (the data comes from the primary), but it also means postgresql.conf/pg_hba.conf tuning done in initdb scripts (like 00_enable_ssl.sh) never runs against the replica's freshly-synced config. Since --write-recovery-conf only appends replication settings, the replica ends up with whatever config was baked into the primary backup. Worth a comment here making that explicit, or confirming that the base image's config is sufficient for the replica to actually serve SSL connections.
| exit 2 | ||
| fi | ||
|
|
||
| echo "INFO: starting initial wal sync" |
There was a problem hiding this comment.
sslmode=require here hard-fails if the primary doesn't have SSL configured. For a replication connection that's fine in this repo (SSL is forced), but the connection string is built by directly interpolating $POSTGRES_REPLICATION_PASSWORD into a URI — if the password ever contains @, : or / this will silently produce a malformed DSN. Consider passing credentials via PGPASSWORD and --host/--username flags instead of embedding them in the URL.
| pg_basebackup \ | ||
| --dbname "postgres://$POSTGRES_REPLICATION_USER:$POSTGRES_REPLICATION_PASSWORD@$POSTGRES_PRIMARY_HOST/postgres?sslmode=require" \ | ||
| --pgdata "$PGDATA" \ | ||
| --progress --verbose --write-recovery-conf --wal-method stream \ |
There was a problem hiding this comment.
Same slot-name collision concern as in 17/entrypoint.sh — slot is named after the replication user, so multiple replicas sharing the user will conflict.
| rlPhaseEnd | ||
|
|
||
| rlPhaseStartTest "Start replication containers" | ||
| rlRun -t -c "docker run -d --name=replica_17 --network=postgres_default -e POSTGRES_REPLICATION_USER=rpl_usr_17 -e POSTGRES_REPLICATION_PASSWORD=replicate-me -e POSTGRES_PRIMARY_HOST=postgres_17 postgres-postgres_17:latest" |
There was a problem hiding this comment.
Two fixed sleep 120 calls add ~4 minutes to every CI run and will still be flaky on a slow runner (pg_basebackup of a freshly-initialised Kiwi TCMS DB is usually fast, but not guaranteed). Poll instead: loop on docker logs replica_17 | grep -q 'completed initial wal sync' (or check pg_isready / SELECT pg_is_in_recovery() inside the container) with a timeout, so the test proceeds as soon as the replica is actually ready.
| rlRun -t -c "docker run -d --name=replica_17 --network=postgres_default -e POSTGRES_REPLICATION_USER=rpl_usr_17 -e POSTGRES_REPLICATION_PASSWORD=replicate-me -e POSTGRES_PRIMARY_HOST=postgres_17 postgres-postgres_17:latest" | ||
| sleep 120 | ||
| rlRun -t -c "docker logs replica_17" | ||
|
|
There was a problem hiding this comment.
The replicas are started with docker run against the compose-created network, but they never get cleaned up if an earlier phase fails — rlPhaseStartCleanup only runs on the happy path in some beakerlib configs. Consider registering the replica containers for cleanup the same way the compose stack is, or at least docker rm -f replica_17 replica_18 defensively at the start of the phase in case a previous run left them behind.
| EOSQL | ||
| } | ||
|
|
||
| replication_user "rpl_usr_17" "replicate-me" |
There was a problem hiding this comment.
Both replication users are created with the same hard-coded password replicate-me, and the file is mounted into the primary unconditionally. Anyone who can reach the primary's port 5432 now has replication credentials baked into the image's compose setup. At minimum, source these from environment variables (e.g. POSTGRES_REPLICATION_PASSWORD_RPL_USR_17) so production deployments aren't stuck with a password that's committed to git; and consider scoping the users' pg_hba.conf entries to the replica subnet rather than relying on the default.
| db17_data: | ||
| db18_data: | ||
|
|
||
|
|
There was a problem hiding this comment.
Pinning the network name to postgres_default makes the test's --network=postgres_default work, but it also means this compose project can no longer be run twice side-by-side on the same host (both will try to claim the same network) and it overrides compose's project-name isolation. Since the test already knows the compose project, it could discover the network dynamically (docker network ls --filter name=_default) or the replicas could be added to the compose file under a profile instead — either avoids hard-coding the name.
|
Example #4 , same review with Claude Opus 5:
results in review immediately below; not inline like others; took a lot more time & $ |
|
Reviewed at 6b95d40. The overall shape is right: wrap the upstream entrypoint, bootstrap with Blocking1.
I'd make the failure explicit: if ! pg_basebackup ... ; then
echo "ERROR: initial wal sync failed"
exit 3
fiAdding 2. The env var guards read unset variables
3. The replication password gets written into
export PGPASSWORD="$POSTGRES_REPLICATION_PASSWORD"
pg_basebackup --dbname "postgres://$POSTGRES_REPLICATION_USER@$POSTGRES_PRIMARY_HOST/postgres?sslmode=require" ...
4.
Options: drop Related, and worth at least a comment in the file: a slot whose replica never comes back pins WAL on the primary until the disk fills. Also, keying the slot name off Test coverage5. "Start replication containers" cannot fail
Worth asserting on something real, e.g. 6. 240 seconds of unconditional Two 7. Cleanup fails when the test aborts early
8. Hardcoded That tag is derived from the Compose project name, which is derived from the directory name. Anyone checking out into a directory not named 9. Replica startup is sequenced before the restart/stop/kill phases The replicas come up at line 40 and the content assertion is at line 69, with Smaller things10. 11. 12. 13. 14. 15. The 16. Renaming the workflow jobs breaks required status checks. 17. The three new env vars are undocumented. There is no README in the repo, so there is nowhere obvious to put them, but 18. Pre-existing, but adjacent: |












No description provided.