Files
seaweedfs/sw-block/design/v2-engine-maintainer-tutorial.md
T
pingqiuandClaude Opus 4.6 d2d57851b0 feat: rebuild MVP — dual-lane session with bitmap protection
Rebuild session protocol implementation for v2-rebuild-mvp-session-protocol.md.

New files:
- rebuild_bitmap.go: RebuildBitmap — session-scoped dense bitset for
  WAL-applied LBA tracking. MarkApplied on local WAL write (not receive).
  ShouldApplyBase returns false for WAL-covered LBAs (WAL always wins).

- rebuild_session.go: RebuildSession — replica-side two-line rebuild.
  WAL lane (ApplyWALEntry) + base lane (ApplyBaseBlock) with bitmap
  conflict resolution. TryComplete requires BOTH base_complete AND
  wal_applied_lsn >= target_lsn. Volume-level control surface:
  StartRebuildSession, ApplyRebuildSessionWALEntry/BaseBlock,
  MarkRebuildSessionBaseComplete, TryCompleteRebuildSession,
  CancelRebuildSession, ActiveRebuildSession.

- rebuild_mvp_test.go: 4 correctness tests — base+WAL converge,
  WAL-applied never overwritten by base, bitmap set on applied not
  received, control surface start/supersede/complete.

- rebuild_transport_test.go: 2 transport-level tests — two-line with
  real WAL shipping, live writes during base copy with bitmap conflict.

Design docs:
- v2-rebuild-mvp-session-protocol.md: MVP spec with message set, apply
  rules, completion/failure/crash rules, test matrix
- v2-sync-recovery-protocol.md: full protocol context (keepup/catchup/
  rebuild unified design, primary decision logic, two-line model)
- v2-session-protocol-shape.md: protocol shape overview

Protocol engine (reference, not production):
- sw-block/protocol/: 7-event engine with ~300 lines, 13 tests

6 rebuild tests pass, all existing component tests pass.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 14:30:34 -07:00

8.3 KiB
Raw Blame History

V2 Engine — Maintainer Tutorial

Audience: engineers taking over sw-block/engine/replication and its integration in weed/server.

Goal: know where truth lives, how to read the code in order, and where to add new rules without breaking layering.


1. Mental model (keep this picture)

flowchart LR
  subgraph Control[Cluster control plane]
    M[Master: heartbeat, assignment, epoch]
  end

  subgraph Local[One volume server — local semantics]
    CE[CoreEngine: per-volume readiness, boundaries, mode, commands]
    OR[RecoveryOrchestrator + Registry + Sender/Session]
  end

  subgraph Muscle[Data plane — weed/storage/blockvol]
    BP[WAL, flush, shipper, receiver, barrier]
  end

  M -->|AssignmentDelivered / events derived from control| CE
  CE -->|Command| BP
  OR -->|execution APIs| BP
  BP -->|observations as Event| CE
  • CoreEngine: one reducer per volume — Event in → updated VolumeState + Command + PublicationProjection. No I/O.
  • Sender / Session / Registry / RecoveryOrchestrator: per-replica recovery authority — session phases, fencing, catch-up/rebuild execution, handshake from RetainedHistory.
  • blockvol / server: muscles execute commands and report facts; they must not silently fork “publication truth” outside events.

See also: v2-protocol-truths.md, v2-two-loop-protocol.md.


2. Repository map (what file does what)

Area Primary paths Responsibility
Package entry & invariants sw-block/engine/replication/doc.go Read first — lists ownership/fencing rules.
Core shell (Phase 14) engine.go, state.go, event.go, command.go, projection.go Volume-level mode, readiness, boundaries, emitted commands.
Per-replica recovery sender.go, session.go, registry.go, budget.go, rebuild.go, outcome.go, history.go Session FSM, handshake classification, bounded catch-up.
Orchestration orchestrator.go, driver.go, executor.go ProcessAssignment, ExecuteRecovery, stepwise recovery plans.
Boundaries adapter.go StorageAdapter — engine never reaches into storage directly.
Runtime helpers engine/replication/runtime/*.go Pending/step execution helpers — not the semantic core.
Host integration weed/server/volume_server_block.go, block_recovery.go, block_protocol_state.go, weed/storage/blockvol/v2bridge Wires engine, applies observations, executes commands.

3. Suggested first read order (~12 hours)

  1. doc.go — invariant list (what must stay true).
  2. types.goSessionKind, SessionPhase, ReplicaState, Endpoint.
  3. event.go + command.go — vocabulary of the core: what can be observed, what can be decided.
  4. state.goVolumeState, ReadinessView, BoundaryView, commandState (idempotence keys).
  5. engine.goApplyEvent, recompute, applyAssignment, primaryEligibleForPublish, bootstrapReason.
  6. sender.go (execution APIs + SessionSnapshot) — how session fencing works.
  7. registry.go + orchestrator.go — how assignments become senders and recovery runs.
  8. Host: grep ApplyEvent / v2Core / applyCoreEvent in weed/server to see how events are produced.

4. Where to add a “new rule” (decision tree)

Ask: what kind of rule is it?

Your change is about… Put it in… Typical pattern
When the volume is publish_healthy / bootstrap_pending, or how readiness/boundary combine CoreEnginerecompute, primaryEligibleForPublish, or new/extended Event handling Add/adjust Event, update ApplyEvent branch, extend recompute; keep commands pure (no I/O).
When a replica may receive live WAL tail, catch-up bounds, session invalidation Sender / Session and/or RecoveryOrchestrator Extend phase checks, checkAuthority, handshake/budget; do not duplicate mode logic in blockvol.
Gating execution (e.g. live ship) from host-visible engine snapshots weed/server (e.g. protocol execution sync) — derive from engine/registry, set policy on BlockVol Keep engine free of TCP; host binds policy to data plane.
Cluster-wide who is primary / epoch Master / assignment path — not inside CoreEngine alone VS consumes assignment as AssignmentDelivered (or equivalent adapter event).

Rule of thumb: if the rule needs only local observations already modeled as Event, it belongs in engine.go. If it needs per-replica session identity or LSN ranges, it belongs in sender.go / session.go. If it needs disk retention / pins, use StorageAdapter and RecoveryDriver paths.


5. Checklist: adding a new CoreEngine Event

  1. Define the type in event.go — implement VolumeID() string.
  2. Add handling in ApplyEvent in engine.go — update VolumeState fields only; no side effects.
  3. If the event implies work for the host, emit a Command (see command.go) or reuse an existing one.
  4. Call recompute(st) if you added fields that affect Mode / Publication (or rely on final recompute at end of ApplyEvent — today every path ends with recompute).
  5. Extend VolumeState.Snapshot() in state.go if you added new copyable state.
  6. Add/extend tests: phase14_*_test.go or focused tests in engine package.
  7. Wire the host: wherever the observation is detected in weed/server, enqueue CoreEngine.ApplyEvent (or the projects adapter) so the event stream is complete.

6. Checklist: changing Mode or Publication semantics

  • Read recompute and bootstrapReason end-to-end — they are the single place for outward mode naming on the bounded path.
  • If you add a new ModeName, add it in state.go and handle it in recompute (and any projection consumers).
  • Do not infer publish health only from shipper logs in random packages — align with primaryEligibleForPublish or deliberately extend it with new Events (e.g. new boundary).

7. Integration: weed/server expectations

  • Assignments from master should eventually surface as AssignmentDelivered (or the unified adapter equivalent) with correct epoch and replica IDs.
  • Observations (receiver ready, shipper configured/connected, barrier OK/fail, LSN advances) must be turned into events; missing events ⇒ core state diverges from reality.
  • Commands returned by ApplyEvent must be executed or explicitly dropped by policy — silent ignore leads to stuck bootstrap_pending.

Grep starting points: v2Core, ApplyEvent, applyCoreEvent, coreProj.


8. Testing strategy (short)

Layer What to prove
engine package tests Deterministic transitions: given event list ⇒ final VolumeState / projection.
Sender/session tests Session ID fencing, phase transitions, budget escalation.
weed/server tests Host wiring: policy + observations ⇒ expected gating or readiness.

See v2-proof-and-retest-pyramid.md.


9. Common pitfalls

  • Duplicating “truth” — updating readiness or durable LSN only in ad-hoc variables without emitting Event.
  • Treating WriteLBA success as commit — core publish_healthy requires DurableLSN > 0 for primary path via primaryEligibleForPublish; align client docs with barrier/group commit semantics.
  • Mixing Core and Sender rules — mode in CoreEngine, per-replica execution in Sender; avoid cross-importing the wrong way from blockvol.
  • Breaking idempotencecommandState tracks what was already commanded; new commands need stable keys (epoch + replica + target LSN where applicable).

  • v2-automata-ownership-map.md — who owns which automaton.
  • v2-session-protocol-shape.md — current VS-to-VS sync/session/data surface.
  • v2-rebuild-mvp-session-protocol.md — implementation target for the first rebuild MVP.
  • v2-protocol-aware-execution.md — host-side execution gating.
  • wal-replication-v2-state-machine.md — replica FSM (design-level).
  • engine/replication/doc.go — source-level invariant list (always keep in sync when you change semantics).