Repository navigation
Checkpoint admission applies copy bounds, so each seq row keeps one writer - #3649
Conversation
A base copy writes an origin's rows without log entries, so no route certified from at or below the highest copied version holds that origin complete. readOriginStates now reads every peer's [copyBound, peerId] row and treats a covered certifiedFrom as absent (a malformed row fails closed). Replication no longer has to rewrite other routes' seq rows, which raced this updater's snapshot commit. A capture carries the origin-state epoch it read first, and certifiedFloors drops one the epoch has passed. A revocation naming no generation clears a level-1 floor instead of revoking it for good, and a route mark can restore a level-2 route's fullCoverage. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017xoBA9XmjPA2mM5joANzNm Dispatch-Task: harper-pro-1092-row-ownership
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017xoBA9XmjPA2mM5joANzNm Dispatch-Task: harper-pro-1092-row-ownership
A copy can restore this node's own lost rows, which its own log then lacks: an own boundary at or below that bound, or a whole-log replay, is refused. A copy bound row must be a plain record of canonical non-negative origin ids and non-negative versions; anything else is a 410, so the client resubscribes rather than retrying an internal error. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017xoBA9XmjPA2mM5joANzNm Dispatch-Task: harper-pro-1092-row-ownership
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017xoBA9XmjPA2mM5joANzNm Dispatch-Task: harper-pro-1092-row-ownership
It used to win on its higher floor and then disqualify the origin, although another route of the same generation qualified. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017xoBA9XmjPA2mM5joANzNm Dispatch-Task: harper-pro-1092-row-ownership
There was a problem hiding this comment.
Code Review
This pull request introduces copy bounds handling for audit-less base copies to ensure subscription checkpoints are complete and correct, updating the design documentation, table resource handling, and checkpoint admission logic, alongside adding corresponding tests and benchmarks. The review feedback highlights a potential upgrade compatibility issue where pre-upgrade level-1 floors marked as revoked could remain permanently floorless, suggests refactoring routeCertifies to safely handle undefined copy bounds lookup, and recommends checking nodeState.fullCoverage !== true instead of === false to robustly restore full coverage when the initial state is undefined.
| if (generation === undefined && typeof nodeState.originGeneration !== 'string') { | ||
| // a level-1 floor has no generation to stay revoked within: clear it, and a later certificate floors again | ||
| if (nodeState.closedFloor !== undefined || nodeState.relayable) { | ||
| nodeState.closedFloor = undefined; | ||
| nodeState.relayable = false; | ||
| originCursorsChanged = true; | ||
| } | ||
| continue; | ||
| } |
There was a problem hiding this comment.
There is a potential upgrade compatibility issue here. If a pre-upgrade build already marked a level-1 [seq, P] row as floorRevoked: true, it will stay floorless permanently. This is because the certificate block at line 2311 skips any later certificate if nodeState.floorRevoked === true, and since level-1 floors do not have a generation, generationChanged is never triggered to clear floorRevoked. Consider adding a mechanism to clear floorRevoked for level-1 floors when a new level-1 certificate is received.
| const routeCertifies = (node: any) => | ||
| isValidFloor(node.closedFloor) && | ||
| typeof node.originGeneration === 'string' && | ||
| node.fullCoverage === true && | ||
| node.partialHistory !== true && | ||
| node.hole !== true && | ||
| node.floorRevoked !== true && | ||
| typeof node.certifiedFrom === 'number' && | ||
| !(node.certifiedFrom > node.closedFloor) && | ||
| !(node.certifiedFrom <= copyBounds.get(node.id)!); |
There was a problem hiding this comment.
The helper function routeCertifies can be simplified and made safer by extracting the copyBounds.get(node.id) lookup into a local variable. This avoids the non-null assertion operator ! (which is a type lie since the bound can be undefined) and avoids implicit loose comparisons with undefined (e.g., node.certifiedFrom <= undefined which implicitly evaluates to false). Using == null is also the preferred idiom for nullish checks in this repository.
const routeCertifies = (node: any) => {
const bound = copyBounds.get(node.id);
return (
isValidFloor(node.closedFloor) &&
typeof node.originGeneration === 'string' &&
node.fullCoverage === true &&
node.partialHistory !== true &&
node.hole !== true &&
node.floorRevoked !== true &&
typeof node.certifiedFrom === 'number' &&
!(node.certifiedFrom > node.closedFloor) &&
(bound == null || node.certifiedFrom > bound)
);
};References
- Loose equality == null / != null is the intentional idiom for null-or-undefined checks in this repository. (link)
- Ensure helper functions are self-safeguarding by incorporating defensive null/undefined checks (e.g., using explicit
== nullchecks) rather than assuming callers have already validated the inputs.
| if ( | ||
| fullCoverage === true && | ||
| typeof nodeState.originGeneration === 'string' && | ||
| nodeState.fullCoverage === false | ||
| ) { |
There was a problem hiding this comment.
If nodeState.fullCoverage is undefined (which is its initial state or when cleared), the condition nodeState.fullCoverage === false will evaluate to false, preventing it from being updated to true. Changing the check to nodeState.fullCoverage !== true makes the restoration of full coverage more robust.
if (
fullCoverage === true &&
typeof nodeState.originGeneration === 'string' &&
nodeState.fullCoverage !== true
) {The transaction committed but its position and route state did not, and the loop only logged it, so a source that settles what it attached at commit counted a hole or revocation as recorded. The end_txn's onFailure now runs (and may hold the stream) before the error is rethrown. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017xoBA9XmjPA2mM5joANzNm Dispatch-Task: harper-pro-1092-row-ownership
A source that releases in onFailure what onCommit would have (an outstanding-commit count, a backlog pause) released it twice. The third argument, true here, says onCommit already ran and only what the end_txn attached for recording was lost. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017xoBA9XmjPA2mM5joANzNm Dispatch-Task: harper-pro-1092-row-ownership
…t was Otherwise a repeat of the same position reads as not advancing and its write is skipped. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017xoBA9XmjPA2mM5joANzNm Dispatch-Task: harper-pro-1092-row-ownership
… a test Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017xoBA9XmjPA2mM5joANzNm Dispatch-Task: harper-pro-1092-row-ownership
⊙ Problem
A base copy writes rows without log entries, so a route that certified their origin from below those rows is no longer complete. Admission reads only
[seq, peer]rows, so replication rewrote other routes' rows to void such starts. Core's sequence updater commits from an earlier read and silently overwrote those voids (harper-pro#1092).🤔 Should the reader apply the copy bound, rather than a writer voiding route rows?
Yes. A copy's effect is database-wide, and the route it voids may be disconnected, with no updater of its own. Reading the bound at admission keeps one writer per
[seq, peer]row and needs no lock on the replicated apply path.💡 Solution
[copyBound, peer]row and ignore a coveredcertifiedFrom, this node's own origin included. A malformed row fails with a 410.fullCoverage.end_txn'sonFailure(withafterCommit), so a source that settled route state at commit can restore it.⚖️ Alternatives
__dbis__is WAL-on, and column families flush independently. Tracked in harper-pro#1134.🔧 Changes
resources/subscriptionCheckpoint.ts:readCopyBounds(strict row shape); the bound check in route selection; the own-origin start and its admission check; the capture's epoch. Look hardest at route selection.resources/transactionBroadcast.ts:certifiedFloorsdrops a capture the epoch has passed.resources/Table.ts:onFailure(error, position, true)when the sequence-row write fails, with the stream's last sequence id rolled back; inupdateRecordedSequenceId, the level-1 revocation clear and thefullCoverageroute-state element.unitTests/resources/subscriptionCheckpoint.bench.js: the read-cost bench, added tonpm run benchinpackage.json.resources/DESIGN.md: the one-writer rule and the admission rule.✅ Verification
subscribeCheckpoint.test.js(admission refuses a covered route, capture issues nothing under the bound, a route that starts above the bound is admitted again, an own-origin bound, six malformed row shapes as 410s) andreplicationSeqCursor.test.js(level-1 clear then re-floor; coverage restore only for a matching level-2 row). Both fail on base core and pass with this change.npm run test:unit:resources: 5070 passing. The 4 failures are the four full-text native suites, which fail identically on base core with the locally installed@harperfast/fulltextpackage.Read cost (bench host, turbo off, 3 interleaved runs)
unitTests/resources/subscriptionCheckpoint.bench.js:captureCertifiedFloorsover 16 peer rows of 16 level-2 origins each.The read runs once per 5 s capture tick and once per checkpoint resume.
🤖 Generated by Claude Opus 5.5; posted via @kriszyp.
Release target: main — ships with harper-pro#1092, which is the only producer of copy bounds and level-2 route marks
Related PRs: #3469 overlaps, #3494 overlaps, #3504 overlaps (audit-less copy apply and commit-before-sequence ordering), #3605 overlaps (both register a bench in package.json), 15 others independent
Deferred, each filed
Deferred, each filed. Admission items go to the stacked admission-hardening work rather than widening this PR:
6ed82865c0362e2e,56b9969961f1f0cb.1f5d09ccc6af379f.Deferred findings, each filed
Deferred findings
6ed82865c0362e2eminor data-integrity —resources/subscriptionCheckpoint.ts— Stale-capture drop and replay recheck depend on harper-pro writer committing bound before copied row and bumping epoch — filed Enforce the copy-bound ordering contract in core, where admission reads it #36471f5d09ccc6af379fminor reliability —resources/Table.ts— Level-1 seq row latched floorRevoked:true by a pre-upgrade build is never cleared; the route cannot certify that origin again — filed A level-1 seq row revoked by an earlier build never takes a floor again #3648d77bb44f8670cac9minor correctness —resources/subscriptionCheckpoint.ts— Capture qualifies certifiedFrom against raw floor; folded boundary S makes admission 410 the node's own fresh token — NOT FILED56b9969961f1f0cbnit data-integrity —resources/Table.ts— Replay recheck and stale-capture drop depend on the harper-pro writer committing the bound before the copied row and bumping the epoch — filed Enforce the copy-bound ordering contract in core, where admission reads it #3647792157686c563d60nit performance —resources/subscriptionCheckpoint.ts— Per-call Map/Buffer/closure allocations in readCopyBounds/readOriginStates — NOT FILEDb2e21a27610c15cdnit reliability —resources/Table.ts— lastSequenceId advanced before the data commit stays set when the commit or onCommit fails and the source moves past — NOT FILED67486c147c73ef9enit reliability —resources/Table.ts— Companion onFrameFailure logs and meters a committed transaction as failed when only its cursor write failed — NOT FILEDcb91c0012ad49d49nit reliability —resources/Table.ts— Sources without onFailure are not told of a cursor-write failure — NOT FILEDLate Grok findings
6 surviving (highest minor) — Grok reviewed 7e92ec1, ruled against a9ae1b6
resources/Table.ts:2343— A level-1[seq, P]row that a pre-upgrade build already markedfloorRevoked: truestays floorless permanently. Main's revocation loop (seen in the standalonedev/harpercheckout) set the flag with no generation check. After upgrade,:2311skips every later certificate for that row.:2345sresources/subscriptionCheckpoint.ts:259— Two checks depend on the harper-pro copy-bound writer doing two things in order: committing[copyBound, peer]before the copied row, then callingbumpOriginStateEpoch. The checks are the stale-capture drop (certifiedFloorsatresources/transactionBroadcast.ts:208) and the admitted-replay recunitTests/resources/subscribeCheckpoint.test.js:525— The test writes the bound row itself and bumps the epoch by hand. Nothing in core proves the real writer-to-reader ordering end to end.resources/subscriptionCheckpoint.ts:108—readCopyBoundsis exported but only used inside its own module. The companion may duplicate the reader with skip-on-malformed semantics instead of this fail-closed one.resources/subscriptionCheckpoint.ts:110— Each scan allocates range objects and therouteCertifiesclosure. Runs once per 5 s capture and once per resume, so off the hot path;subscriptionCheckpoint.bench.jsnow covers this scan.resources/Table.ts:2402— Added comments that narrate the adjacent code:resources/Table.ts:2402,unitTests/resources/replicationSeqCursor.test.js:365,unitTests/resources/subscribeCheckpoint.test.js:536,unitTests/resources/subscriptionCheckpoint.bench.js:1-2.Not standing: 6 resolved, 2 merged. Grok ran 19m (19m from launch to report); the fold adjudication (
cursor-grok@7e92ec115b33-745ecacf, claude) took 2m.Review-Coverage: authored=claude; ran=cursor-composer,gemini,codex,cursor-grok; late=cursor-grok:adjudicated; adjudicated=domain; declined=muse; rounds=6; full=2 @ 43ebf79
Review-Attention: study ~20m (critical: Table.ts, transactionBroadcast.ts; decisions: copy-bound-read-side, malformed-bound-fails-db-wide, level1-revocation-clears, coverage-restore-via-route-mark, do-less-alternative) @ 43ebf79