feat(storage): add PostgreSQL EnsureSchema bootstrapper - #946
Conversation
Postgres counterpart to the MySQL schema bootstrap: existence-only convergence that creates missing storage tables from the embedded Postgres schema files, serialized across pods by the advisory-lock locker with a fast-path existence check re-verified under the lock. Deliberately no column/index drift repair — Spirit's diffing is MySQL-only, so schema evolution on an already-bootstrapped Postgres store lands separately (PLAT-38417).
There was a problem hiding this comment.
Pull request overview
Adds PostgreSQL support to SchemaBot’s startup storage-schema bootstrap by introducing a Postgres-specific EnsureSchema path (existence-only table creation) and routing schema.DialectPostgres to it, bringing Postgres storage bootstrapping in line with the existing MySQL experience.
Changes:
- Route
EnsureSchemato a new PostgreSQL bootstrapper whenWithDialect(schema.DialectPostgres)is selected. - Implement
ensurePostgresSchemato create missing storage tables from embedded Postgres DDL under a cross-pod advisory lock (re-checking under lock; one transaction per table). - Add unit + integration coverage for routing, statement splitting, embedded schema reading, idempotence, missing-table repair, and concurrent pod startup behavior.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| pkg/api/ensure_schema.go | Adds Postgres dialect routing and updates the fail-closed error to list supported dialects. |
| pkg/api/ensure_schema_test.go | Updates fail-closed test to use a truly unsupported dialect and adds a routing test for Postgres. |
| pkg/api/ensure_schema_postgres.go | New Postgres bootstrapper: reads embedded schema files, checks for missing tables, serializes via advisory lock, and creates tables transactionally. |
| pkg/api/ensure_schema_postgres_test.go | Unit tests for Postgres schema file splitting and embedded schema file reading invariants. |
| pkg/api/ensure_schema_postgres_integration_test.go | Integration tests validating fresh bootstrap, idempotence, repair of missing tables, and safe concurrent bootstraps using a Postgres container. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…apper Delete the hand-rolled statement splitter (pgx's simple query protocol executes multi-statement zero-argument Execs natively), pin the lock contention deterministically via pg_locks, extract the shared Postgres testcontainer starter, and align the flow's logging, timeout docs, and operator docs with the MySQL bootstrapper.
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
🤖 Adversarial correctness review, requested by Armand and performed by his agent. Reviewed at head Verdict: clean — no fix-before-merge findings. The existence-only scope matches the settled direction for this slice (create-if-absent bring-up now; declarative diff/evolution lands separately with its own mechanism), the concurrency story is right, and the pieces I attacked hardest all held. One design note, non-blocking: existence-only convergence means a table that exists with the wrong shape is accepted silently at startup and surfaces later as runtime query errors (e.g. a binary rollback after a newer binary bootstrapped a newer-shape table, or the documented new-column-on-existing-table gap). That's the deliberate, documented bound — and detecting drift properly is exactly the deferred diff mechanism, so building it here would be scope creep. If a cheap startup-time tripwire ever becomes worth it before the real mechanism lands, the new plain-statements lint invariant makes a column-name presence check against Verified solid:
Build, unit ( This review was generated by Claude Code (claude-fable-5). |
aparajon
left a comment
There was a problem hiding this comment.
🤖 Approving per the adversarial review above — clean, no blocking findings. Stamped by Armand's agent (claude-fable-5).
Summary
Adds the PostgreSQL
EnsureSchemabootstrapper — the Postgres counterpart to the MySQL schema bootstrap.EnsureSchemanow routes thepostgresstorage dialect to a Postgres-specific bootstrap path instead of failing closed.What
ensurePostgresSchema: existence-only convergence — creates missing storage tables from the embedded Postgres schema files (feat(storage): add PostgreSQL storage-table schema definitions #936), serialized across pods by the advisory-lock locker (feat(namedlock): add PostgreSQL advisory-lock implementation #938,namedlock.Postgres), with a fast-path existence check re-verified under the lock.Why
A Postgres-backed state store must be able to bootstrap its own schema the same way the MySQL store does.
Deliberately no column/index drift repair on existing Postgres tables: the MySQL bootstrapper's drift repair rides on Spirit's diff/apply, which is MySQL-only. Schema evolution on an already-bootstrapped Postgres store is tracked separately under PLAT-38417.
References
namedlock.Postgres)