Skip to content

feat(findings): replica_identity_missing — a published table that can't be updated - #113

Merged
alexshapalov merged 1 commit into
pgrundev:mainfrom
lofoneh:feat/replica-identity-finding
Sep 30, 2026
Merged

alexshapalov merged 1 commit into
pgrundev:mainfrom
lofoneh:feat/replica-identity-finding

Conversation

@lofoneh

@lofoneh lofoneh commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor

What

A table published for UPDATE or DELETE needs a replica identity so the subscriber
can find the row. Without one, Postgres refuses the write:

ERROR: cannot update table "events" because it does not have a replica identity
       and publishes updates

Reads and INSERTs keep working, so you don't find out at deploy time — you find out
on the first UPDATE after the migration. This adds a finding for it.

It reads the catalog only — pg_publication, pg_publication_tables, pg_class,
pg_namespace, pg_index — so it's schema scope and pgbot lint
catches it on an empty CI database. Insert-only publications need no identity and are
skipped. I deliberately didn't flag every table without a primary key — that fires on
append-only tables and partitions where it's intended.

Note: #46 also bumps the schema to 1.4.0. Whichever lands second needs a renumber, and
I'm happy to rebase.

Verification

On PG 18.6:

  • The integration fixture creates the publication, confirms the UPDATE really fails,
    then asserts the finding. Adding a primary key clears both.
  • The schema-profile acceptance test still sees zero findings on a sound schema.
  • Doc-verify runs the new verify query.
  • pgbot lint exits 2 with the finding, and cleanly once there's a key.
  • scripts/gate.sh green.

Checklist

  • scripts/gate.sh passes (builds HEAD, not just the working tree)
  • New SQL is read-only; no EXPLAIN ANALYZE; findings stay deterministic (computed in Go)
  • No PII enters a model.Context / --json / the store — relation and publication names only
  • --json change is additive, model.SchemaVersion bumped + schema regenerated
  • A new finding has a docs/findings/<id>.md page + catalog entry

@lofoneh

lofoneh commented Sep 29, 2026

Copy link
Copy Markdown
Contributor Author

I ran this against a wal_level=logical server and established ground truth by running the real UPDATE on each table first, so the report is checked against what Postgres does rather than what I assumed:

table real UPDATE reported
primary key succeeds no
no primary key fails yes (d)
REPLICA IDENTITY NOTHING fails yes (n)
identity index dropped fails yes (i)
insert-only publication n/a no
partitioned, publish_via_partition_root partition write fails yes (root)

The dropped-index case is the one I wouldn't have guessed: after DROP INDEX, relreplident stays i with nothing behind it, so the table quietly stops accepting updates. FOR ALL TABLES and FOR TABLES IN SCHEMA both expand correctly, and a table outside the published schema isn't flagged. With every case fixed, lint exits 0.

@alexshapalov

Copy link
Copy Markdown
Contributor

@lofoneh, tnx, pls resolve conflicts

…'t be updated

A table published for UPDATE or DELETE needs a replica identity so the
subscriber can find the row. Without one Postgres rejects the write itself:
"cannot update table … because it does not have a replica identity and
publishes updates". Reads and INSERTs keep working, so the failure lands on
the first UPDATE after a migration, not at deploy time.

A catalog-only gauge collector reads pg_publication and pg_class: DEFAULT with
no primary key, NOTHING, or USING INDEX whose index is gone or invalid.
Insert-only publications need no identity and are not reported. Scope is
schema, so it runs under --profile=schema and pgbot lint on an empty CI
database — the migration-PR path where this is worth catching.

The integration fixture proves the UPDATE fails before asserting the finding,
then that a primary key clears both.

New replica_identity section in --json; SchemaVersion 1.5.0 (additive) — pgrundev#38
took 1.4.0 first, so this is the next minor.
@lofoneh
lofoneh force-pushed the feat/replica-identity-finding branch from f41442c to a0c07f0 Compare September 30, 2026 02:49
@lofoneh

lofoneh commented Sep 30, 2026

Copy link
Copy Markdown
Contributor Author

@alexshapalov , Conflicts resolved. #38 took 1.4.0, so this is 1.5.0 now and the schema's regenerated.

@alexshapalov
alexshapalov merged commit c133885 into pgrundev:main Sep 30, 2026
20 checks passed
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.

2 participants