pgjdbc/pgjdbc GitHub issues and pull requests (mirror)  
help / color / mirror / Atom feed
From: vlsi (@vlsi) <[email protected]>
To: pgjdbc/pgjdbc <[email protected]>
Subject: [pgjdbc/pgjdbc] PR #4189: feat(test): opt-in per-worktree isolation for the test Postgres container
Date: Mon, 15 Jun 2026 19:07:46 +0000
Message-ID: <[email protected]> (raw)

## Why

`docker/postgres-server/docker-compose.yml` is shared across git worktrees and plain checkouts. Compose derives the project name from the directory (`postgres-server`), so every worktree is treated as the **same** project: shared network, shared container, and `run --rm`/`down` in one worktree tears down the server another is using. The published ports (5432/5433/5434) are fixed too, so two servers cannot run side by side.

This bites anyone running work in parallel across worktrees — for example several branches, or multiple agents, sharing one machine.

## What

Opt-in isolation, gated by a single environment variable. Default behaviour is unchanged.

- `docker/bin/postgres-server`: when `PG_ISOLATE=1` is set, the script derives a unique `COMPOSE_PROJECT_NAME` and a host-port offset from `git rev-parse --show-toplevel` (a `cksum` of the worktree path → slot 0–199 → offset ×10). With `PG_ISOLATE` unset it keeps the historic project name and ports `5432/5433/5434`, so CI and existing setups are untouched. An explicit `PG_PUBLISH_PORT` still wins.
- `docker-compose.yml`: the port mappings now substitute `PG_PUBLISH_PORT` / `PG_REPLICA_ONE_PUBLISH_PORT` / `PG_REPLICA_TWO_PUBLISH_PORT` (with the old defaults), so a direct `docker compose up` honours them as well.
- Fix a copy-paste bug: the second replica port read `PG_REPLICA_ONE_PUBLISH_PORT` instead of `PG_REPLICA_TWO_PUBLISH_PORT`.

The variable works as a global toggle: export `PG_ISOLATE=1` once in your shell profile or a `direnv` `.envrc`, and every worktree gets its own container and ports automatically.

## How to verify

```bash
cd docker/postgres-server

# default — ports 5432/5433/5434, project "postgres-server"
docker compose config | grep -E 'name:|published:'

# isolated — distinct ports and project name
PG_ISOLATE=1 ../bin/postgres-server   # logs the chosen project and ports, then starts
```

## Draft — open questions

This is a draft to settle the design before finishing. Deliberately **not** included yet:

- Writing the chosen ports into `build.local.properties`, so `./gradlew test` in a worktree hits its own server. Without it, the tests still target 5432 when isolation is on.
- Slot-collision handling. Two worktrees whose paths hash to the same `cksum % 200` would share ports. The robust alternative is ephemeral host ports read back via `docker compose port`, at the cost of being harder to write into `build.local.properties` up front.

Alternatives considered (Gradle shared `BuildService`, Testcontainers) are heavier and change the CI flow; happy to discuss if reviewers prefer one of those.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

diff --git a/docker/bin/postgres-server b/docker/bin/postgres-server
index d9fb4dd137..34e0f8bdf2 100755
--- a/docker/bin/postgres-server
+++ b/docker/bin/postgres-server
@@ -48,12 +48,34 @@ log () {
 main () {
     local publish_port="${PG_PUBLISH_PORT:-5432}"
     local replica_one_port="${PG_REPLICA_ONE_PUBLISH_PORT:-5433}"
-    local replica_two_port="${PG_REPLICA_ONE_PUBLISH_PORT:-5434}"
+    local replica_two_port="${PG_REPLICA_TWO_PUBLISH_PORT:-5434}"
 
     # Determine our current directory and change to be in the same directory as the compose file
     local script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
     cd "${script_dir}/../postgres-server"
 
+    # Opt-in per-worktree isolation. Set PG_ISOLATE=1 (for example via direnv or your
+    # shell profile) to give each git worktree its own Compose project and host ports,
+    # so parallel checkouts no longer share or tear down each other's server. The default
+    # (PG_ISOLATE unset) keeps the historic project name and ports 5432/5433/5434, so CI
+    # and existing setups are unaffected. An explicit PG_PUBLISH_PORT still wins.
+    if [[ -n "${PG_ISOLATE:-}" ]]; then
+        local worktree
+        worktree="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
+        local slot
+        slot=$(( $(printf '%s' "${worktree}" | cksum | cut -d' ' -f1) % 200 ))
+        local offset=$(( slot * 10 ))
+
+        publish_port="${PG_PUBLISH_PORT:-$(( 5432 + offset ))}"
+        replica_one_port="${PG_REPLICA_ONE_PUBLISH_PORT:-$(( 5433 + offset ))}"
+        replica_two_port="${PG_REPLICA_TWO_PUBLISH_PORT:-$(( 5434 + offset ))}"
+
+        local slug
+        slug="$(basename "${worktree}" | tr '[:upper:]' '[:lower:]' | tr -c 'a-z0-9_-' '_' | tr -s '_')"
+        export COMPOSE_PROJECT_NAME="${COMPOSE_PROJECT_NAME:-pgjdbc_${slug}_${slot}}"
+        log "Worktree isolation on: project=${COMPOSE_PROJECT_NAME}, ports=${publish_port}/${replica_one_port}/${replica_two_port}"
+    fi
+
     log "Starting Postgres server and mapping to local port ${publish_port}"
     exec docker compose run \
         --rm \
diff --git a/docker/postgres-server/docker-compose.yml b/docker/postgres-server/docker-compose.yml
index 1108aa819e..2ecde90a02 100644
--- a/docker/postgres-server/docker-compose.yml
+++ b/docker/postgres-server/docker-compose.yml
@@ -2,9 +2,9 @@ services:
   pgdb:
     image: postgres:${PGV:-latest}
     ports:
-      - 5432:5432
-      - 5433:5433
-      - 5434:5434
+      - ${PG_PUBLISH_PORT:-5432}:5432
+      - ${PG_REPLICA_ONE_PUBLISH_PORT:-5433}:5433
+      - ${PG_REPLICA_TWO_PUBLISH_PORT:-5434}:5434
     security_opt:
       - seccomp:unconfined
     volumes:


reply

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Reply to all the recipients using the --to and --cc options:
  reply via email

  To: github://pgjdbc/pgjdbc
  Cc: [email protected], [email protected]
  Subject: Re: [pgjdbc/pgjdbc] PR #4189: feat(test): opt-in per-worktree isolation for the test Postgres container
  In-Reply-To: <<[email protected]>>

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox