Message-ID: From: "vlsi (@vlsi)" To: "pgjdbc/pgjdbc" Date: Mon, 15 Jun 2026 19:07:46 +0000 Subject: [pgjdbc/pgjdbc] PR #4189: feat(test): opt-in per-worktree isolation for the test Postgres container List-Id: X-GitHub-Additions: 26 X-GitHub-Author-Id: 213894 X-GitHub-Author-Login: vlsi X-GitHub-Base: master X-GitHub-Changed-Files: 2 X-GitHub-Commits: 1 X-GitHub-Deletions: 4 X-GitHub-Draft: true X-GitHub-Head-Branch: claude/exciting-dijkstra-6984bc X-GitHub-Head-SHA: 533482a50daea4f0499b22f24a099ee4c6979112 X-GitHub-Issue: 4189 X-GitHub-Labels: building-and-testing X-GitHub-Merge-SHA: 87d70235d83703f060e719ef0abc06d14b183068 X-GitHub-Repo: pgjdbc/pgjdbc X-GitHub-State: open X-GitHub-Type: pull_request X-GitHub-Url: https://github.com/pgjdbc/pgjdbc/pull/4189 Content-Type: text/plain; charset=utf-8 ## 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: