Files
seaweedfs/sw-block/design/v2-loop1-surface-draft.md
T
pingqiuandClaude Opus 4.6 b8c6944e3f feat: V2 MVP milestone — masterv2 + volumev2 + in-process failover
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>
2026-04-05 13:08:02 -07:00

254 lines
4.7 KiB
Markdown

# V2 Loop1 Surface Draft
Date: 2026-04-05
Status: active
## Purpose
This note turns the two-loop design into a code-facing draft for the current
`masterv2` and `volumev2` packages.
It does not implement the new protocol yet.
It defines the smallest surface refactor that should happen first.
## Goal
Replace the current mixed `heartbeat -> assignments` MVP surface with three
separate `Loop 1` surfaces:
1. periodic heartbeat
2. promotion query
3. assignment
## Current State
Today, `masterv2` uses one heartbeat type and one assignment type:
- `sw-block/runtime/masterv2/master.go`
- `sw-block/runtime/volumev2/control_session.go`
- `sw-block/runtime/volumev2/volume.go`
The current heartbeat still mixes:
1. applied identity
2. outward projection naming
3. fields that should later become failover evidence
## Target Surfaces
### 1. Heartbeat
Purpose:
1. liveness
2. applied identity
3. outward compressed mode
Recommended fields:
- `NodeID`
- `ReportedAt`
- per-volume:
- `Name`
- `Path`
- `Epoch`
- `Role`
- `Mode`
- `ModeReason`
- passive `CommittedLSN` cache
- `RoleApplied`
- `ReplicaReady`
Not included:
- per-replica progress
- catch-up target
- detailed recovery phase
If `CommittedLSN` is present here, it is only a convenience cache for
`masterv2`. Fresh promotion judgment still uses the query channel.
### 2. Promotion Query
Purpose:
1. fresh promotion evidence
2. failover arbitration
Recommended request:
- `VolumeName`
- `ExpectedEpoch`
Recommended response:
- `VolumeName`
- `NodeID`
- `Epoch`
- `Role`
- `CommittedLSN`
- `WALHeadLSN`
- `ReceiverReady`
- `Eligible`
- `Reason`
Rule:
- `CommittedLSN` is the primary selection key
- `WALHeadLSN` is only a tiebreaker
### 3. Assignment
Purpose:
1. authorize role
2. fence stale owners
3. deliver member identity
Recommended fields:
- `Name`
- `Path`
- `NodeID`
- `Epoch`
- `LeaseTTL`
- `Role`
- `ReplicaSet`
- `CreateOptions`
`ReplicaSet` should carry identity plus transport addresses, not progress.
## Field Migration From Current MVP
### Current `masterv2.VolumeHeartbeat`
Today:
- `Name`
- `Path`
- `Epoch`
- `Role`
- `ProjectionMode`
- `PublicationReason`
- `RoleApplied`
Should become:
- `Name`
- `Path`
- `Epoch`
- `Role`
- `Mode`
- `ModeReason`
- `RoleApplied`
- `ReplicaReady`
Interpretation change:
- `ProjectionMode` should be renamed to `Mode`
- `PublicationReason` should stop pretending to be a generic reason field
- `ReplicaReady` should be explicit on the identity surface
### Current `masterv2.VolumeView`
Today:
- `ObservedEpoch`
- `ObservedRole`
- `ProjectionMode`
- `PublicationReason`
- `RoleApplied`
Should become:
- `ObservedEpoch`
- `ObservedRole`
- `Mode`
- `ModeReason`
- `RoleApplied`
- `ReplicaReady`
Optional later:
- bounded cached failover evidence for debugging only
### Current `volumev2.Node.Heartbeat()`
Current source:
- `snap.Status`
- `snap.Projection`
Recommended extraction:
- `Mode` from `snap.Projection.Mode.Name`
- `ModeReason` from `snap.Projection.Mode.Reason`
- passive `CommittedLSN` cache from local status snapshot
- `RoleApplied` from `snap.Projection.Readiness.RoleApplied`
- `ReplicaReady` from `snap.Projection.Readiness.ReplicaReady`
Not from heartbeat:
- `CatchUpTarget`
- `RecoveryProgress`
`CommittedLSN` may appear in heartbeat as a passive cache only.
Fresh promotion authority still belongs to the promotion-query channel.
`CatchUpTarget` and `RecoveryProgress` belong to Loop 2.
## Code Refactor Order
### Step 1
Add a small shared contract package for `Loop 1` types, for example under:
- `sw-block/runtime/protocolv2/`
Start with:
1. `Heartbeat`
2. `Assignment`
3. `PromotionQueryRequest`
4. `PromotionQueryResponse`
### Step 2
Make `masterv2` use the shared `Loop 1` contract types instead of local ad hoc
duplicates.
### Step 3
Make `volumev2` heartbeat generation write to the narrowed heartbeat shape.
### Step 4
Add a promotion-query interface without implementing full failover yet.
The first code slice only needs:
1. request and response types
2. local evidence extraction helper
3. one focused test proving query returns fresh local evidence
## Test Guidance
The first code refactor should keep existing control-loop tests and add one new
test:
1. existing heartbeat-assignment convergence should still pass
2. new promotion-query test should prove:
- query is separate from heartbeat
- fresh state is returned at call time
- heartbeat `CommittedLSN` is only a passive cache
- `WALHeadLSN` is not part of periodic heartbeat
## Non-Goals
This draft does not yet define:
1. full Loop 2 message schema
2. full new primary truth reconstruction choreography
3. quorum-specific `CommittedLSN` algorithm
Those come after the `Loop 1` surface is cleanly split.