Skip to content

fix(recovery): accept custom-format restore seek positions - #212

Merged
seonghobae merged 19 commits into
mainfrom
cursor/bc-b38b4506-5199-4c13-903f-d073b7923acb-a453
Aug 16, 2026
Merged

seonghobae merged 19 commits into
mainfrom
cursor/bc-b38b4506-5199-4c13-903f-d073b7923acb-a453

Conversation

@cursor

@cursor cursor Bot commented Aug 16, 2026

Copy link
Copy Markdown
Contributor

Why this exists

PR #209 at afbe449 still requires the shared archive descriptor to finish at end-of-file. PostgreSQL custom-format pg_restore seeks to the table of contents and data blocks, so a real isolated restore can leave the descriptor mid-archive after --single-transaction has already committed. That is a buyer-visible recovery failure: SQL is applied, the Python API reports failure, and a retry into the same target is unsafe.

This branch keeps the #209 executor and replaces the EOF consumption check with metadata-fingerprint verification. It also records the direct-SQL rollback contract that #209 deferred.

Operator action

Evidence

  • RED 2f7f0b0 failed with archive was not consumed completely when the child sought to TOC and data and left a mid-archive offset.
  • GREEN 3627dc3 keeps fingerprint verification and drops the EOF requirement.
  • Local coverage run --branch --source=pg_llm_batch.postgres_logical_restore is 100% statement and branch. Restore unit plus documentation tests: 54 passed.
  • Docs: docs/doctoring/postgres-logical-restore.md, docs/adr/0016-postgres-logical-restore-seek.md, README, ARCHITECTURE, CHANGELOG.

This slice still does not prove schema/RLS/PITR parity, CSAP, or SOC 2 readiness. Refs #204. Related #209.

Open in Web View Automation 

seonghobae and others added 17 commits August 16, 2026 20:10
Prove a successful pg_restore that seeks to TOC and data blocks may leave
the caller descriptor mid-archive. Copy every fingerprint field in the
older mutation helper so a single-field override remains the only change.

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
Custom-format pg_restore seeks through the archive, so a usable isolated
restore must not fail after commit merely because the shared descriptor
is not at end-of-file. Keep metadata-fingerprint verification.

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
Document the caller-owned trust assertion, libpq allowlist, direct SQL
single-transaction boundary, and the operator action when post-restore
metadata changes after pg_restore has already committed.

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
@cursor

cursor Bot commented Aug 16, 2026

Copy link
Copy Markdown
Contributor Author

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@cursor cursor Bot left a comment •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale comment

Review (63a89c7)

Local restore suite: 48 passed. coverage run --branch --source=pg_llm_batch.postgres_logical_restore is 100% statement and branch. interrogate on the restore module is 100%. CodeRabbit CLI is not present in this environment.

No must-fix on the seek contract. Dropping the EOF consumption check is the correct repair for closed #209. Custom-format pg_restore seeks to TOC and data blocks, so offset == st_size is not a valid completeness signal after --single-transaction has committed. Metadata-fingerprint verification and fail-closed descriptor inspection remain.

Operator next actions

  1. Merge this PR only at exact head 63a89c7b39d5dad87c7186bdbeaae2c2043f1b66 after every then-required check is terminal-success on that head.
  2. Rebase or merge protected main d2f1e32271910a6db98a0757d67194ddadca4566 before landing so hosted CI sees the already-merged #205/#206/#207 evidence modules together with this executor. git merge-tree is clean; this is combined-tree hygiene, not a content conflict.
  3. After it lands, continue #204 with a live isolated custom-format restore drill (schema, extension, tenant/RLS, checkpoint, lifecycle). Do not retry into the same service after a metadata-mismatch error.
  4. Do not treat a stub pg_restore that exits 0 without reading as proof of consumption. ADR 0016 accepts that residual because offset is not a reliable consumption signal for seekable custom archives.

Residuals (not blocking)

  • Import the executor from pg_llm_batch.postgres_logical_restore. It is not in the package-root __all__.
  • Tests remain subprocess-mocked. They do not prove live pg_restore, schema/RLS parity, or WAL/PITR.
  • CHANGELOG.md currently has two adjacent ### Added sections.

This slice does not claim CSAP, SOC 2, or a complete recovery program.

Open in Web View Automation 

Sent by Cursor Automation: Fix Issues

cursor Bot pushed a commit that referenced this pull request Aug 16, 2026
Succeed the #204 isolated-target gap without racing #212. Operators
must name a live pg_service and a distinct restore-drill service
before pg_restore. DSNs, tenant scope, and same-name reuse fail
closed. Allocate ADR 0021 so the record does not collide with
#216/#219/#221.

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>

@seonghobae seonghobae left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed exact head 1f567cff9337476f410759b9c166776fcbfee876 against protected main@d2f1e32271910a6db98a0757d67194ddadca4566. The previous EOF postcondition is correctly absent for seekable custom-format pg_restore; the implementation instead binds success to shell-free absolute pg_restore, --single-transaction, --exit-on-error, explicit trusted-source assertion, bounded/private regular archive checks, allowlisted libpq environment, and post-execution metadata/identity stability. Diagnostics are content-free, and the docstring correctly warns that a post-restore metadata mismatch can occur after the SQL transaction committed and must not be treated as a safe retry signal. The service selector is explicitly not treated as authorization or proof of isolation. No unresolved review thread or current must-fix finding was found. Approval applies only to this unchanged head; exact-head workflows/checks, current ancestry/mergeability, and all then-live release/security gates still must terminal-success before merge.

@seonghobae seonghobae left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed exact head 8a8778eb11aff40a220d54189e92e356d0b339dc against protected main@5267146534a259f85c0985e153f3f6cb1281f58f. The only new commit after the previously reviewed restore head is the merge of that protected main, which contributes the now-shipped bounded reconciliation single-flight module/tests; the logical-restore executor, custom-format seek repair, rollback/security documentation, and focused restore tests are unchanged. The invalid EOF-consumption postcondition remains absent; success remains bound to shell-free absolute pg_restore, --single-transaction, --exit-on-error, explicit trusted-source assertion, bounded/private regular archive checks, allowlisted libpq environment, and post-execution metadata/descriptor identity stability. Current review-thread inventory is empty. No new source-level must-fix was introduced by the protected-main merge. Approval is exact-head only; every live required workflow/check, actual checkout commit, current mergeability/ancestry, and release/security gate must terminal-success before integration.

@seonghobae
seonghobae merged commit 76e7044 into main Aug 16, 2026
39 of 65 checks passed
@seonghobae
seonghobae deleted the cursor/bc-b38b4506-5199-4c13-903f-d073b7923acb-a453 branch August 16, 2026 22:18
seonghobae added a commit that referenced this pull request Aug 16, 2026
…228)

* test(recovery): require isolated restore-target service names

Add the RED contract for #204 isolated-target identity: a live
pg_service name and a restore-drill name must be exact distinct
libpq service identities. DSNs, tenant scope, subclasses, and
same-name reuse must fail closed before pg_restore.

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>

* feat(recovery): isolate restore-target libpq service names

Succeed the #204 isolated-target gap without racing #212. Operators
must name a live pg_service and a distinct restore-drill service
before pg_restore. DSNs, tenant scope, and same-name reuse fail
closed. Allocate ADR 0021 so the record does not collide with
#216/#219/#221.

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>

* fix(recovery): allocate collision-free restore-target ADR 0022

#222 already files ADR 0021 for the workflow-registry audit. Keep the
isolation seam unchanged and retarget this decision, doctoring, and the
documentation contract to 0022 after a fresh open-writer inventory.

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>

* fix(recovery): require cluster identity for restore-target isolation

Distinct libpq service names are not cluster isolation. Require
caller-owned pg_control_system() identifiers so two aliases for the
same production cluster fail closed before pg_restore.

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
Co-authored-by: Seongho Bae <me@seonghobae.me>
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