Skip to content

verdict: mark a copy-and-swap run that blocked writes - #262

Merged
Kiran01bm merged 4 commits into
mainfrom
kiran01bm/quiesced-offline-marker
Oct 28, 2026
Merged

Kiran01bm merged 4 commits into
mainfrom
kiran01bm/quiesced-offline-marker

Conversation

@Kiran01bm

@Kiran01bm Kiran01bm commented Oct 27, 2026 •

Copy link
Copy Markdown
Collaborator

A copy-and-swap verdict now carries writes_blocked: true when the run blocked the table's writes (--block-writes, offline mode). The dry-run note also stops presenting --block-writes as an equivalent alternative to the live path.

Why

A run that blocks writes is not online: the write fence refuses every write to the table with 55006 until the swap. Today the only trace of this in the verdict is a clause in detail. An orchestrator that sees exit 0 and executed-via-copy-and-swap cannot tell an offline run from an online one without parsing prose. The dry-run note also offered the flag "when nothing writes the table", which reads as an equivalent option rather than an offline mode.

What

  • verdict.Verdict.WritesBlocked (writes_blocked in JSON, omitted when false) is set on the executed verdict of a run that blocked writes. The text renderers print a blocked: line.
  • On a failed verdict the field reports what happened, not the mode the run was asked for. A stopped blocked-writes run reads its hold from what the engine guarantees:
    • An abandoned run had its fence on.
    • A standing checkpoint row means the fence stands, because a fresh run fences before it writes the row.
    • No row means the run stopped before its fence went on, for example at a table lock another session holds. That run reports false, and its detail says the table kept taking writes and that a rerun starts from the beginning.
  • The dry-run note now says the route needs logical decoding, and that --block-writes instead blocks the table's writes until the swap (offline mode).
  • schemabot-integration.md names the field in the copy-and-swap verdict table.
  • Tests:
    • The outcome and run-verdict unit tests check the field for live runs and for runs that blocked writes; they fail without it.
    • A blocked-writes run stopped at a held table lock reports false, leaves no row, and leaves the table writable. A run the host stopped after its fence, and a run abandoned on a data fault, both report true.
    • The CLI copy-and-swap test checks the field in the JSON.
    • The renderer parity fixture sets it.

Before / after

The same retype of public.t (ALTER COLUMN id TYPE bigint), run with --block-writes public.t --json. Before, nothing machine-readable says writes were refused for the whole run; after, the field does.

Before                                          After
------                                          -----
{                                               {
  "outcome": "executed-via-copy-and-swap",        "outcome": "executed-via-copy-and-swap",
  "table": "public.t",                            "table": "public.t",
  "detail": "...; writes to the table were        "detail": "...; writes to the table were
     blocked for the length of the run"              blocked for the length of the run",
                                                  "writes_blocked": true
}                                               }
orchestrator sees exit 0 and the same           orchestrator branches on writes_blocked
outcome as an online run                        to report the run as offline

🤖 Generated with Amp (Claude Opus 4.6)

The verdict's detail said a quiesced run fenced writes only in prose; a
quiesced field lets an orchestrator tell an offline run from an online one.
@Kiran01bm
Kiran01bm marked this pull request as ready for review October 27, 2026 02:49
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ⚠️ Failed 2026-10-27T02:49:31.660169Z
We couldn’t complete this review. Comment @codex review to try again.
d3f7025 Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@JashLal

JashLal commented Oct 27, 2026

Copy link
Copy Markdown
Collaborator

🤖 Adversarial correctness review on Jash's behalf, performed by his AI agent. Reviewed at head d3f7025.

Verdict: the additive result field improves the success contract, with one reporting issue on failures before fencing. The execution path and outcome classification are unchanged; the issue overreports downtime.

Findings

1. [P2] An early failure can claim writes were fenced when no fence was installed (pkg/migrate/copy_swap.go:301). The new field copies the requested Quiesced option for every ordinary Runner error. A fresh invocation can pass preflight and then fail in AcquireTableLock (connection loss/cancellation), or in checkpoint-store initialization, before dispatch ever installs the fence. It then emits quiesced:true and “the table was offline for writes … (writes refused 55006)”, although the table continued taking writes. The new tests exercise completed runs and post-fence divergence, so they miss this case.

Action items

  1. (Finding 1) Distinguish observed fencing from the requested mode when folding early Runner errors, and add a fresh-run pre-fence failure regression. If the field is deliberately a mode declaration, document and render it that way instead of asserting observed downtime.

Verified

Read the full diff and exact-head surrounding Runner/dispatch and renderer code; the done-row outcome stays authoritative, the field is additive with omitempty, both renderers share parity coverage, and no tests were removed or weakened. All 16 reported CI checks passed, including PostgreSQL 14–18. Local build/tests and complete inline-thread inspection were unavailable in this environment; CI is corroboration only.

This review was generated by Codex (GPT-6).

@JashLal

JashLal commented Oct 27, 2026

Copy link
Copy Markdown
Collaborator

🤖 Two-lens product review on Jash's behalf at d3f7025.

OSS adoption: the dry-run explanation now makes the offline write behavior and logical-decoding requirement visible before execution. The explicit verdict line also makes a successful quiesced run easier to recognize.

SchemaBot integration: an additive typed/JSON field is the right adapter seam and removes the need to parse detail for successful runs, without adding an orchestrator dependency to the engine. Treat it as a run-mode disclosure, not current fence status: a failed run may already have abandoned its fence, and a refused resume can retain one. The early-failure semantics flagged in the correctness review should be resolved before consumers interpret it as evidence that downtime actually occurred. No additional product blocker found; consumer integration was not run locally.

This review was generated by Codex (GPT-6).

@JashLal JashLal left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

🤖 Approving on Jash’s behalf under the authorized review workflow at d3f7025. The P2 reporting issue remains: pre-fence failures can report observed downtime solely from the requested quiesced option. Clarify requested versus observed mode and cover the early-failure path. Approval does not mean the finding is resolved. No local tests were run. Generated by Codex.

Matches the --block-writes flag, so the field says what happened to the
table rather than what the operator declared about it.
Keeps both verdict text lines where they met: this branch's blocked:
line and main's allowed: line (#253), in the CLI renderer and in
verdict.String.
@Kiran01bm Kiran01bm changed the title verdict: mark a quiesced copy-and-swap run as offline for writes verdict: mark a copy-and-swap run that blocked writes Oct 28, 2026
A blocked-writes run that failed before its fence went on, such as at a
table lock another session holds, reported the table as offline although
it kept taking writes. A stopped run now reads its hold from the engine:
an abandoned run or a standing checkpoint row means the fence went on, no
row means it did not.
@Kiran01bm

Copy link
Copy Markdown
Collaborator Author

🤖 Adversarial review response — created by Kiran's code review agent (Amp, Claude Opus 4.6) — pull/262, follow-up commit

The P2 is fixed: a failed verdict now reports writes_blocked only when the run's fence actually went on, and a regression test covers a run that stops before it. Since the review, the field has been renamed from quiesced to writes_blocked, and --quiesced to --block-writes (#260).

# Finding Status Explanation
C1-F1 A blocked-writes run that fails before its fence is installed (for example at AcquireTableLock) still reports the table as offline fixed Reproduced: with another session holding the table lock, the run failed with the field true while the table kept taking writes. A stopped blocked-writes run now reads its hold from what the engine guarantees, not from the requested mode. An AbandonedError means the fence was on, since the copy runs behind it. A standing checkpoint row means the fence stands, because a fresh run fences before it writes the row and takes the fence back off when the row does not land. No row means the fence never went on, so the field is false and the detail says the table kept taking writes and a rerun starts from the beginning. If the row cannot be read, the field stays true and the read error joins the run's. New TestBlockWritesRunStoppedBeforeItsFenceDoesNotReportWritesBlocked. The host-stop and data-fault-abandon tests now assert true. Four mutants of the read were each killed.
C2 Treat the field as a run-mode disclosure, not current fence status, and resolve the early-failure semantics fixed The field now means "this run's fence went on". Whether the fence still stands is in detail, which separates a standing fence (resume or --restart) from one the abandon took off. Both are documented on Verdict.WritesBlocked and in schemabot-integration.md.
Review Approval with the P2 open no action The P2 is addressed above.

Source: block/pg-sprite#262, review comments 6433951237 and 6433951607 and review 5662303529 at head d3f70253; fixes in the follow-up commit.

@Kiran01bm
Kiran01bm enabled auto-merge (squash) October 28, 2026 05:12
@Kiran01bm
Kiran01bm merged commit 0d96b2d into main Oct 28, 2026
16 checks passed
@Kiran01bm
Kiran01bm deleted the kiran01bm/quiesced-offline-marker branch October 28, 2026 05:22
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