Repository navigation
Count only resident peers toward replicatedConfirmation - #1032
Conversation
createConfirmationWaiter now takes the per-residency lists of other resident peers that core passes for the written records, and resolves only once every list has the requested number of confirmed members. A non-resident peer (sent an invalidation stub, or nothing) passes the database watermark without holding the record and no longer counts. Tables without residency lists keep the database-wide count. Bumps core for the residency hand-off and pre-commit fail-fast. Refs #1031 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AtkQzQLoohXK3gEMzGFRz8 Dispatch-Task: hp-1031-residency-aware-confirmation
…cy confirmation test Bumps core for the pre-staging refusal and the post-commit refusal path. Refs #1031 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AtkQzQLoohXK3gEMzGFRz8 Dispatch-Task: hp-1031-residency-aware-confirmation
Refs #1031 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AtkQzQLoohXK3gEMzGFRz8 Dispatch-Task: hp-1031-residency-aware-confirmation
Waits for the tested writes to be visible on the writer before issuing the probe write C holds, so C confirming the probe proves C applied them. Bumps core for the confirmation-version fix. Refs #1031 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AtkQzQLoohXK3gEMzGFRz8 Dispatch-Task: hp-1031-residency-aware-confirmation
A record homed on a node that is no longer a cluster member, or on a peer this node does not send the database to, could never be confirmed and waited out the 900 s timeout. The existing recipient guard now also requires each resident list to have the count among this node's recipients. Refs #1031 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AtkQzQLoohXK3gEMzGFRz8 Dispatch-Task: hp-1031-residency-aware-confirmation
…-submission refusal Refs #1031 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AtkQzQLoohXK3gEMzGFRz8 Dispatch-Task: hp-1031-residency-aware-confirmation
…ist Sets Pre-push review: the waiter and the reachability guard allocated a Set per resident list (and a recipient-name Set) for every confirmed transaction. Core already deduplicates each list, so membership is counted over the arrays directly. Refs #1031 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AtkQzQLoohXK3gEMzGFRz8 Dispatch-Task: hp-1031-residency-aware-confirmation
Refs #1031 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AtkQzQLoohXK3gEMzGFRz8 Dispatch-Task: hp-1031-residency-aware-confirmation
Refs #1031 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AtkQzQLoohXK3gEMzGFRz8 Dispatch-Task: hp-1031-residency-aware-confirmation
Refs #1031 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AtkQzQLoohXK3gEMzGFRz8 Dispatch-Task: hp-1031-residency-aware-confirmation
Refs #1031 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AtkQzQLoohXK3gEMzGFRz8 Dispatch-Task: hp-1031-residency-aware-confirmation
Plain loops instead of per-call closures in the confirmation predicate and the recipient lookup. Refs #1031 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AtkQzQLoohXK3gEMzGFRz8 Dispatch-Task: hp-1031-residency-aware-confirmation
…ey target Refs #1031 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AtkQzQLoohXK3gEMzGFRz8 Dispatch-Task: hp-1031-residency-aware-confirmation
…s feasibility hook The recipient bound (count against this node's recipients, and against each written record's residents among them) is now registered as core's pre-submission feasibility check instead of running in the post-commit confirmation callback, so its 400 no longer arrives for a write that already landed. Bumps core for the committed-log-key target, unique residency ids and the hook. Refs #1031 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AtkQzQLoohXK3gEMzGFRz8 Dispatch-Task: hp-1031-residency-aware-confirmation
Refs #1031 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AtkQzQLoohXK3gEMzGFRz8 Dispatch-Task: hp-1031-residency-aware-confirmation
…target Refs #1031 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AtkQzQLoohXK3gEMzGFRz8 Dispatch-Task: hp-1031-residency-aware-confirmation
… target Refs #1031 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AtkQzQLoohXK3gEMzGFRz8 Dispatch-Task: hp-1031-residency-aware-confirmation
Refs #1031 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AtkQzQLoohXK3gEMzGFRz8 Dispatch-Task: hp-1031-residency-aware-confirmation
…heck Refs #1031 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AtkQzQLoohXK3gEMzGFRz8 Dispatch-Task: hp-1031-residency-aware-confirmation
Refs #1031 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AtkQzQLoohXK3gEMzGFRz8 Dispatch-Task: hp-1031-residency-aware-confirmation
Refs #1031 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AtkQzQLoohXK3gEMzGFRz8 Dispatch-Task: hp-1031-residency-aware-confirmation
…resident rejoins Bumps core for the late-requested confirmation target. Refs #1031 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AtkQzQLoohXK3gEMzGFRz8 Dispatch-Task: hp-1031-residency-aware-confirmation
There was a problem hiding this comment.
Code Review
This pull request updates the replication confirmation logic to ensure that replicatedConfirmation only counts peers that are resident for the written records. It introduces resident peer lists tracking in createConfirmationWaiter and adds pre-submission feasibility checks to refuse impossible confirmation counts early. The changes are accompanied by comprehensive unit and integration tests, as well as updated design documentation. Feedback on the changes highlights a potential TypeScript compilation error under strictNullChecks in replication/knownNodes.ts where residentPeerLists is mapped directly without being narrowed; using the already-narrowed residentGroups variable is suggested to resolve this.
Refs #1031 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AtkQzQLoohXK3gEMzGFRz8 Dispatch-Task: hp-1031-residency-aware-confirmation
…eout message Refs #1031 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AtkQzQLoohXK3gEMzGFRz8 Dispatch-Task: hp-1031-residency-aware-confirmation
…irst-submission feasibility check Refs #1031 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AtkQzQLoohXK3gEMzGFRz8 Dispatch-Task: hp-1031-residency-aware-confirmation
Refs #1031 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AtkQzQLoohXK3gEMzGFRz8 Dispatch-Task: hp-1031-residency-aware-confirmation
⊙ Problem
replicatedConfirmation: Ncounts any peer whose applied position for the database has reached the write. On a table with a residency list, that includes peers that do not hold the record. A non-resident peer is sent only an invalidation stub, and a peer excluded byreplicateTo(implemented as residency) may be sent nothing for that record. Both still pass the database position. A write could resolve "confirmed by N" while no other node held a complete copy. Refs #1031.💡 Solution
Core (Count only a record's residents toward replicatedConfirmation — harper#3148) now passes the confirmation callback one list of other resident peers for each distinct residency among the written records, and targets the committed transaction's log key.
createConfirmationWaiterresolves only when every list has N confirmed members. A peer still counts once, by identity (harper-pro#633), and thealreadyConfirmedPeersseed goes through the same check. The writer decides who is eligible and reuses the existingCOMMITTED_UPDATEacknowledgement, so receivers of any version need nothing new. When there are no lists (a table without residency), the waiter keeps the database-wide count and thegetReplicationRecipientsbound as before. The recipient guard moves into core's new pre-submission feasibility hook (assertConfirmationFeasible), so its 400 arrives before the write lands rather than after it. It also checks each list against this node's recipients (assertResidentsReachable): a record homed on a node that is no longer a cluster member, or on a peer this node does not send the database to, is refused at once instead of waiting out the 900 s timeout.Before → after, with three nodes W, B, C, a record homed on [W, B], B down, and
X-Replicate-To: *;confirm=1:confirm=2on that recordresident on 1 other node(s) (B)), nothing written[W, B, retired-node],confirm=2⚖️ Alternatives
Framing-Verdict: chosen-approach-sound. The core framing recheck (adopted there) moved this PR's recipient guard into core's pre-submission feasibility hook.Weighed in the core PR: per-record acknowledgements from receivers would need a protocol change and break rolling upgrades. Having pro re-read residency from the committed entry would race concurrent overwrites. Both were rejected.
🔧 Changes
replication/knownNodes.tscreateConfirmationWaitertakesresidentPeerLists. Its predicate resolves once every list has N confirmed members, without allocation (countMembers). ThealreadyConfirmedPeersseed goes through the same predicate.assertConfirmationFeasibleis the recipient bound plusassertResidentsReachable. It is registered as core's pre-submission feasibility check, so its 400 arrives before the write lands.replication/DESIGN.md: item 29 and a test-table row.core: bumped to the harper#3148 branch head.✅ Verification
integrationTests/cluster/residencyConfirmation.test.mjswith the fixture (config.yaml,resources.js,schema.graphql): three nodes, writes through REST. The operations API runs on the main thread, which loads no applicationsetResidency.confirm=2on a record homed on[W, B], and on[W, B, retired-node]: immediate 400, and nothing is written.confirm=1: a record homed on[W, B], anOpenrecord withX-Replicate-To: B, and a two-record PUT ([W,B]+[W,C]). The test waits until all are committed on W. A later probe homed on[W, C]confirms, which proves C applied them. All three stay unconfirmed for 3 s. After B restarts and rejoins the mesh, all three confirm, and B holds the full records.Passes 3/3 in repeated runs. On base it fails both tests: all three writes were confirmed with B down, and
confirm=2was accepted with 204.unitTests/replication/confirmationWaiter.test.mjs: 33 passing. Covers non-resident ignored, seed filtered, per-record lists, one peer counted toward several lists, shortfall message,[]treated as database-wide, andassertResidentsReachable.npm run test:unit: passing. Existing confirmation cluster suites unchanged and passing:fullyConnectedReplication,replicationTopology,nonReplicatedDatabase,selectiveTableSubscription,removeNodeBlastRadius,replicationWorkerPool.🤖 Generated by Claude (Anthropic, Opus 5.5); posted via @kriszyp.
Depends-on: HarperFast/harper#3148
Related PRs: #1030 independent (set the base core pin this bumps from)
Complexity: complicated
Origin — the dispatch brief this PR was written from
replicatedConfirmation counts only peers that hold a copy of the record
Fix harper-pro#1031:
replicatedConfirmation: Nmust count only peers that hold a complete copy of the written record(s). TodaycreateConfirmationWaiterinreplication/knownNodes.tscounts any peer whose per-database applied watermark (CONFIRMATION_STATUS_POSITION) reaches the write'stxnTime, so a non-resident peer (received only an INVALIDATED stub) or areplicateTo-excluded peer (received nothing) is counted. This is a coordinated core + pro change: core supplies the per-write resident peer set to thereplicationConfirmation(...)callback; pro's waiter ignores peers outside it. Use the coordinated-PR flow (harper-pro worktree with its owncoresubmodule checkout; harper PR + harper-pro PR with the bumpedcorepointer). Open both as drafts.Acceptance
replicatedConfirmation: 1is not confirmed by a non-resident or replicateTo-excluded peer that has advanced its watermark.coreto the harper PR's commit; PR bodies reference harper-pro#1031 and note documentation#723 needs updating after merge.Dispatch: task
hp-1031-residency-aware-confirmation· queued by kris-session · ran by claude/opus/high · worker kzyp-xps-1Review-Coverage: authored=claude; ran=cursor-composer,gemini,codex; adjudicated=domain; declined=cursor-grok,cursor-kimi,cursor-muse; rounds=11; full=5 @ d1cf078
Review-Attention: study ~20m (critical: knownNodes.ts; decisions: per-residency-each-meets-count, pre-submission-feasibility-hook, direct-recipients-only, writer-side-eligibility, do-less-alternative) @ d1cf078