V2 runtime packages: - sw-block/runtime/masterv2: identity authority (desired state, heartbeat handling, promotion arbitration via SelectPromotionCandidate) - sw-block/runtime/volumev2: per-volume micro-cluster shell (node, orchestrator, control session, iSCSI frontend, takeover gate, failover session + driver, replica summary reconstruction) - sw-block/runtime/purev2: RF1 execution shell (engine + store + dispatcher + local boundary observations) - sw-block/runtime/protocolv2: three-channel separation (heartbeat/assignment/query + replica summary) V2 binaries: - sw-block/cmd/v2singleblock: single-node RF1 block server - sw-block/cmd/purev2rf1: minimal RF1 runtime binary Milestone capabilities: - RF1 write/read/sync with engine-driven mode projection - masterv2 ↔ volumev2 heartbeat convergence + assignment reissue - Promotion query with fresh CommittedLSN/WALHeadLSN evidence - Replica summary for bounded takeover reconstruction - Primary-loss reconstruction from peer summaries (fail-closed gate) - In-process failover driver with session observability - Local boundary observations feed engine (Committed/Durable/Checkpoint) Design docs: - v2-two-loop-protocol.md: identity vs data-control separation - v2-automata-ownership-map.md: event/command ownership split - v2-loop1-surface-draft.md: heartbeat/query/assignment field spec - v2-volumev2-single-node-mvp.md: target layering - v2-kernel-closure-review.md: per-volume micro-cluster principle - v2-pure-runtime-rf1-bootstrap.md, v2-capability-map.md, v2-proof-and-retest-pyramid.md Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
9.5 KiB
V2 Two-Loop Protocol
Date: 2026-04-05 Status: active
Purpose
This note fixes the protocol boundary for the next V2 step.
The goal is not to finalize every wire field before implementation. The goal is to make the ownership boundary stable enough that automata, constraints, and runtime packages can be reorganized without mixing identity control and replication consensus again.
Core Rule
The protocol is split into two loops:
Loop 1: identity controlLoop 2: data control
These loops must not be collapsed into one heartbeat or one state owner.
Authority Principle
Each volume should be treated as a small distributed cluster:
masterv2is the identity authority outside the cluster- the selected primary is the data-control authority inside the cluster
- replicas report bounded facts to the primary, not full truth to
masterv2
This means:
masterv2decides who is allowed to own the role- the new primary decides how takeover, catch-up, and rebuild proceed
masterv2may query bounded facts for arbitration, but it does not choreograph data recovery step by step
Loop 1: Identity Control
Owner:
masterv2 <-> volumev2
Frequency:
- low
- heartbeat scale
- assignment scale
- promotion-query scale
Three Control Channels
Within Loop 1, the control plane should be split into three different
channels. They must not be collapsed into one message type.
1. Heartbeat
Direction:
volumev2 -> masterv2
Frequency:
- periodic
- lightweight
Purpose:
- liveness detection
- compressed outward mode
- confirmation that assignment was applied
Heartbeat should carry only:
NodeID- per-volume
Mode - applied
Epoch - applied
Role RoleAppliedReplicaReady- optional passive
CommittedLSNcache
Heartbeat should not carry:
- per-replica progress
- catch-up targets
- rebuild detail
- full failover evidence
If CommittedLSN is carried in heartbeat, it is only a passive cache. The
authoritative failover-time value still comes from promotion query.
2. Promotion Query
Direction:
masterv2 -> candidate volumev2- candidate
volumev2 -> masterv2
Frequency:
- on demand
- only during failover or promotion arbitration
Purpose:
- obtain fresh failover evidence
- avoid treating stale heartbeat cache as authority
Candidate response should include:
CommittedLSNWALHeadLSNas a weaker tiebreakerEpochRoleReceiverReady- bounded eligibility reason if not promotable
The promotion query is where fresh identity-loop evidence is collected. It is not a replacement for the data-control loop, and it is not a continuous replication-progress feed.
If heartbeat also carries CommittedLSN, promotion query still wins whenever
fresh arbitration is required.
3. Assignment
Direction:
masterv2 -> volumev2
Frequency:
- on demand
- role change or membership change
Purpose:
- authorize role ownership
- fence stale owners
- deliver replica-set identity
Master To Volume
masterv2 -> volumev2 carries only:
EpochRoleLeaseTTLReplicaSetidentities and addresses
It does not carry:
- per-replica progress
- catch-up target history
- detailed rebuild plan
Volume To Master
volumev2 -> masterv2 carries only bounded identity evidence:
- applied
Epoch - applied
Role - outward
Mode RoleAppliedReplicaReady
This channel must remain lightweight. Fresh failover evidence belongs to the promotion-query channel, not the periodic heartbeat.
Loop 2: Data Control
Owner:
primary engine <-> replica engine
Frequency:
- high
- write scale
- barrier scale
- reconnect scale
This is where replication consensus lives.
Primary To Replica
Primary-side data-control messages should cover:
- WAL entry stream
- barrier request with
Epochand target LSN - reconnect or resume handshake
- rebuild/catch-up execution requests when needed
Replica To Primary
Replica-side data-control messages should cover:
FlushedLSN- bounded status such as
ok,epoch_mismatch,timeout,fsync_failed - reconnect gap evidence
- coarse local recovery state
Primary-Owned Per-Replica State
The primary brain should own:
ReplicaFlushedLSNShippedLSNas diagnostic only- replica
State CatchUpTargetRetentionFloorLastContactTime
Role Of Masterv2
masterv2 authorizes:
- who is primary
- who is replica
- which epoch is active
- when stale owners must be fenced
masterv2 must not decide:
- replay from LSN
XtoY - whether the next action is
keepuporcatchup - how rebuild is executed
- continuous commit progress
masterv2 may query candidates for fresh promotion evidence, but it still does
not become the owner of replication history.
Reconstruction And Takeover
Promotion and reconstruction are related, but they do not have the same owner.
What Masterv2 Leads
masterv2 leads:
- failover detection
- epoch fencing
- candidate query for fresh promotion evidence
- primary selection
- assignment of the new primary role
What The New Primary Leads
The selected replacement primary leads:
- local assignment realization
- collection of self and peer replica summaries
- bounded truth reconstruction
- fail-closed takeover gating
- follow-on
keepup,catchup, orrebuildorchestration
Rule
masterv2 may say:
- "you are now the authorized primary candidate for epoch
E" - "these are the members of the replica set"
But masterv2 must not say:
- "replay from LSN
XtoY" - "use replica
Ras the rebuild source" - "enter
catchupbeforerebuild" - "the cluster is safe because my last cached view looked healthy"
Role Of Primary Brain
The primary brain discovers:
- current replica state
- gap or retention situation
- barrier success or failure
- whether the volume is
keepup,catchup,degraded, orneeds_rebuild
The primary brain decides:
- keep shipping
- start catch-up
- escalate to
needs_rebuild - start rebuild after role/assignment allows it
Distributed State-Machine Rules
1. Different Nodes Have Different Views
Each node must distinguish:
- local execution truth
- last observed peer truth
- cluster identity truth from
masterv2
Do not collapse these into one blob.
2. All Peer Observations Are Epoch-Scoped
Any peer observation that affects recovery must be tied to:
Epoch- session or generation token
Old-epoch observations must be ignored or fail closed.
3. New Primary Reconstructs Truth
After failover, the new primary must rebuild its own data-control truth from:
- local state
- peer summaries
- reconnect handshakes
It must not trust masterv2 as a cache of full recovery history.
It may use masterv2 only as the source of authorization and replica identity.
4. Outward Mode Is Compressed Evidence
allocated_only, bootstrap_pending, publish_healthy, degraded, and
needs_rebuild are public meanings, not the full internal recovery automaton.
5. Ambiguity Fails Closed
When barrier lineage, progress lineage, or epoch lineage is unclear, the system must prefer:
degradedneeds_rebuild- no promotion without enough eligibility evidence
Constraint Migration
Most of the last week's V2 work remains valid. The important change is where each constraint belongs.
Keep As-Is
These constraints still stand:
- epoch fencing
- one active session per replica per epoch
catchupandrebuildare different paths- fail closed on ambiguous recovery truth
- semantics first, adapters later
Move To Loop 1
These belong to identity control:
- assignment application
- role ownership
- lease ownership
- stable replica identity and addressing
- compressed heartbeat evidence
- on-demand promotion query for fresh evidence
Move To Loop 2
These belong to data control:
- committed/durable/checkpoint boundaries
- barrier result meaning
- replica progress
- keepup/catchup/rebuild progression
- retention-floor and catch-up targeting
Existing V2 Seeds To Reuse
The current V2 code already has the right seeds for the primary-led loop:
sw-block/engine/replication/state.gosw-block/engine/replication/event.gosw-block/engine/replication/command.gosw-block/engine/replication/sender.gosw-block/engine/replication/session.gosw-block/engine/replication/registry.go
The current MVP already has the right seeds for the identity loop:
sw-block/runtime/masterv2/master.gosw-block/runtime/volumev2/control_session.gosw-block/runtime/volumev2/orchestrator.go
Immediate Next Step
Before deeper implementation, the codebase should next define:
- the minimal
Loop 1contract types in code, split into heartbeat, promotion query, and assignment - the minimal
Loop 2progress and reconnect contract draft - the automata ownership map showing which engine events and commands belong to identity control versus data control
Promotion Logic
Promotion should use fresh on-demand evidence, not stale heartbeat cache.
Recommended judgment order:
- fence the old primary by epoch
- query all surviving candidates
- reject any candidate with wrong epoch, wrong role lineage, or not-ready receiver state
- choose the candidate with highest
CommittedLSN - use
WALHeadLSNonly as a tiebreaker for equally committed candidates - assign new primary role at a new epoch
This keeps the durability boundary centered on CommittedLSN, which is the
last LSN that satisfied the configured durability mode such as sync_all or
sync_quorum.