Skip to content

Switch the PostgreSQL driver from pgx to pgGo - #114

Merged
alexshapalov merged 3 commits into
mainfrom
pggo-migration
Sep 30, 2026
Merged

alexshapalov merged 3 commits into
mainfrom
pggo-migration

Conversation

@alexshapalov

Copy link
Copy Markdown
Contributor

Summary

PgBot now talks to PostgreSQL through pgGo v0.1.0, a dependency-free wire-protocol client (standard library only), instead of pgx. pgx, pgpassfile, pgservicefile and puddle leave the module graph.

Ported:

  • The connection layer: pool, session pins, read-only transactions, the SSH DialFunc, the pooler/PgDog probes, and the Aurora --all-instances host override.
  • The collectors, erd, logs, advise (EXPLAIN (GENERIC_PLAN) via the simple protocol), and the MCP tools.
  • The integration tests.

The full audit of what PgBot uses, and how pgGo covers it, is in pggo/docs/pgbot-compatibility.md.

Verification

  • Unit tests (with -race) pass.
  • Integration suite passes on PostgreSQL 13, 14, 15, 16, 17, 18 and 19beta1, over TLS (SCRAM; MD5 on 13).
  • Previously skipped tests now run and pass: the pooler tests through a real PgBouncer 1.25 in transaction mode and PgDog, with pgbench write load.
  • Real commands, side by side: the pgx build and the pgGo build were run against the same databases (PG 13–19, a streaming PG 18 standby, PgBouncer, PgDog). The commands were inspect (JSON and text), activity, queries, indexes, tables, vacuum, waits, erd, lint, tune, logs, advise, and the MCP tools schema_of / explain_plan / vacuum_health. Every command except inspect produced identical output on every target.
  • inspect differs only in PgBot's own footprint on an otherwise idle database. pgGo pipelines BEGIN with each collector's first query, so PgBot's own commits are mostly not yet flushed to pg_stat_database when the sample window closes. Against a rate-limited 200 TPS pgbench workload, pgx-PgBot reported about 220 TPS and pgGo-PgBot about 200. Cache hit and rollbacks were identical. This is documented in the CHANGELOG.
  • The binary is about 5 MB smaller: 26.9 MB → 21.8 MB.
  • The integration tests run faster: 45.5 s → 14.8 s across 93 tests.

Notes

  • Test fixtures: fixtures that sent several statements in one Exec (a pgx simple-protocol behavior) now use SimpleQuery explicitly. No assertion was weakened.
  • Transaction poolers: no protocol fallback is needed any more, because pgGo only uses unnamed statements parsed and executed in one sync. The prepared-statement probe still runs and still counts as a pooler signal.
  • Known gap: libpq multi-host DSNs (host=a,b) worked implicitly under pgx and are not supported by pgGo. They are not a documented PgBot feature; such a DSN now fails loudly instead of silently picking a member.
  • Pre-existing test issue: TestIntegration_selfExclusion_wiring fails identically on pgx and pgGo when other sessions are active (for example, pgbench load or PgBouncer's pooled server connections). It assumes a quiet database, so it is not a migration regression.
  • CI: the integration matrix gains 19beta1.

🤖 Generated with Claude Code

alexshapalov and others added 3 commits September 29, 2026 20:04
pgbot now talks to PostgreSQL through github.com/pgrundev/pggo, a
dependency-free wire-protocol client. The connection layer (pool, session
pins, read-only transactions, SSH DialFunc, pooler/PgDog probes), the
collectors, erd, logs, advise and the MCP tools are ported; pgx,
pgpassfile, pgservicefile and puddle leave the module graph.

Output is unchanged apart from pgbot's own footprint in sampled counters
(see CHANGELOG), verified side by side against PostgreSQL 16-19, a
streaming standby, PgBouncer and PgDog. The unit and integration suites
pass on 16-19 over TLS+SCRAM, including the pooler tests.

go.mod temporarily replaces pggo with ../../pggo until pggo's library
release is tagged.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Drops the local replace directive. The integration matrix gains
19beta1, where the pgGo migration was verified.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Since PostgreSQL 15 a backend flushes its counters to pg_stat_database
only when it goes idle, so the commits inside one long DO block stay
invisible until the block ends. On 15+ the 'non-zero TPS under load'
guard therefore only ever saw pgbot's own traffic: the pgx build passed
by counting itself (and would have passed with the stats-caching bug it
exists to catch); the pgGo build, which generates less self-traffic in
the sample window, correctly saw 0 TPS. pgbench runs the same INSERTs as
client transactions, which are counted.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@alexshapalov

alexshapalov commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor Author

The first CI run failed TestIntegration_ratesArePresent on PG15+ (PG14 passed). Root cause: CI's write load was one long DO loop, and since PostgreSQL 15 a backend flushes its counters to pg_stat_database only when it goes idle, so those commits were invisible while the test sampled. Reproduced locally: during the loop, xact_commit moved by exactly 1 per sample (the sampler itself).

On 15+ the guard had therefore only ever seen pgbot's own traffic. Under the same DO-block load, the pgx build passes by counting itself, and would also have passed with the stats-caching bug the guard exists to catch. The pgGo build, which leaves less self-traffic in the window, correctly saw 0 TPS.

Fix (86cb1fa, CI only): the load is now pgbench running the same INSERTs as client transactions. The assertion is unchanged. Both the pgx and the pgGo builds pass it locally, and all 21 checks pass here.

@alexshapalov
alexshapalov merged commit 96df3d4 into main Sep 30, 2026
21 checks passed
@alexshapalov
alexshapalov deleted the pggo-migration branch September 30, 2026 03:16
elkaix added a commit to elkaix/pgbot that referenced this pull request Oct 1, 2026
pgrundev#113 landed after pgrundev#114 removed pgx from go.mod, so the test's pgx import
left the collect package unbuildable under go vet / go test ./...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant