Copies of design docs removed in Phase 09, preserved in sw-block/docs/archive/ for historical reference. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
13 KiB
Phase 07 Service-Slice Plan
Date: 2026-03-30
Status: historical phase-planning artifact
Scope: Phase 07 P0
Purpose
Define the first real-system service slice that will host the V2 engine, choose the first concrete integration path in the existing codebase, and map engine adapters onto real modules.
This is a planning document. It does not claim the integration already works.
Decision
The first service slice should be:
- a single
blockvolprimary on a real volume server - with one replica target (
RF=2path) - driven by the existing master heartbeat / assignment loop
- using the V2 engine only for replication recovery ownership / planning / execution
This is the narrowest real-system slice that still exercises:
- real assignment delivery
- real epoch and failover signals
- real volume-server lifecycle
- real WAL/checkpoint/base-image truth
- real changed-address / reconnect behavior
It is narrow enough to avoid reopening the whole system, but real enough to stop hiding behind engine-local mocks.
Why This Slice
This slice is the right first integration target because:
weed/server/master_grpc_server.goalready delivers block-volume assignments over heartbeatweed/server/master_block_failover.goalready owns failover / promotion / pending rebuild decisionsweed/storage/blockvol/blockvol.goalready owns the current replication runtime (shipperGroup, receiver, WAL retention, checkpoint state)- the existing V1/V1.5 failure history is concentrated in exactly this master <-> volume-server <-> blockvol path
So this slice gives maximum validation value with minimum new surface.
First Concrete Integration Path
The first integration path should be:
- master receives volume-server heartbeat
- master updates block registry and emits
BlockVolumeAssignment - volume server receives assignment
- block volume adapter converts assignment + local storage state into V2 engine inputs
- V2 engine drives sender/session/recovery state
- existing block-volume runtime executes the actual data-path work under engine decisions
In code, that path starts here:
- master side:
weed/server/master_grpc_server.goweed/server/master_block_failover.goweed/server/master_block_registry.go
- volume / storage side:
weed/storage/blockvol/blockvol.goweed/storage/blockvol/recovery.goweed/storage/blockvol/wal_shipper.go- assignment-handling code under
weed/storage/blockvol/
- V2 engine side:
sw-block/engine/replication/
Service-Slice Boundaries
In-process placement
The V2 engine should initially live:
- in-process with the volume server /
blockvolruntime - not in master
- not as a separate service yet
Reason:
- the engine needs local access to storage truth and local recovery execution
- master should remain control-plane authority, not recovery executor
Control-plane boundary
Master remains authoritative for:
- epoch
- role / assignment
- promotion / failover decision
- replica membership
The engine consumes these as control inputs. It does not replace master failover policy in Phase 07.
Control-Over-Heartbeat Upgrade Path
For the first V2 product path, the recommended direction is:
- reuse the existing master <-> volume-server heartbeat path as the control carrier
- upgrade the block-specific control semantics carried on that path
- do not immediately invent a separate control service or assignment channel
Why:
- this is the real Seaweed path already carrying block assignments and confirmations today
- this gives the fastest route to a real integrated control path
- it preserves compatibility with existing Seaweed master/volume-server semantics while V2 hardens its own control truth
Concretely, the current V1 path already provides:
- block assignments delivered in heartbeat responses from
weed/server/master_grpc_server.go - assignment application on the volume server in
weed/server/volume_grpc_client_to_master.goandweed/server/volume_server_block.go - assignment confirmation and address-change refresh driven by later heartbeats in
weed/server/master_grpc_server.goandweed/server/master_block_registry.go - immediate block heartbeat on selected shipper state changes in
weed/server/volume_grpc_client_to_master.go
What should be upgraded for V2 is not mainly the transport, but the control contract carried on it:
- stable
ReplicaID - explicit
Epoch - explicit role / assignment authority
- explicit apply/confirm semantics
- explicit stale assignment rejection
- explicit address-change refresh as endpoint change, not identity change
Current cadence note:
- the block volume heartbeat is periodic (
5 * sleepInterval) with some immediate state-change heartbeats - this is acceptable as the first hardening carrier
- it should not be assumed to be the final control responsiveness model
Deferred design decision:
- whether block control should eventually move beyond heartbeat-only carriage into a more explicit control/assignment channel should be decided only after the
Phase 08 P1real control-delivery path exists and can be measured
That later decision should be based on:
- failover / reassignment responsiveness
- assignment confirmation precision
- operational complexity
- whether heartbeat carriage remains too coarse for the block-control path
Until then, the preferred direction is:
- strengthen block control semantics over the existing heartbeat path
- do not prematurely create a second control plane
Storage boundary
blockvol remains authoritative for:
- WAL head / retention reality
- checkpoint/base-image reality
- actual catch-up streaming
- actual rebuild transfer / restore operations
The engine consumes these as storage truth and recovery execution capabilities. It does not replace the storage backend in Phase 07.
First-Slice Identity Mapping
This must be explicit in the first integration slice.
For RF=2 on the existing master / block registry path:
- stable engine
ReplicaIDshould be derived from:<volume-name>/<replica-server-id>
- not from:
DataAddrCtrlAddr- heartbeat transport endpoint
For this slice, the adapter should map:
ReplicaID
- from master/block-registry identity for the replica host entry
Endpoint
- from the current replica receiver/data/control addresses reported by the real runtime
Epoch
- from the confirmed master assignment for the volume
SessionKind
- from master-driven recovery intent / role transition outcome
This is a hard first-slice requirement because address refresh must not collapse identity back into endpoint-shaped keys.
Adapter Mapping
1. ControlPlaneAdapter
Engine interface today:
HandleHeartbeat(serverID, volumes)HandleFailover(deadServerID)
Real mapping should be:
- master-side source:
weed/server/master_grpc_server.goweed/server/master_block_failover.goweed/server/master_block_registry.go
- volume-server side sink:
- assignment receive/apply path in
weed/storage/blockvol/
- assignment receive/apply path in
Recommended real shape:
- do not literally push raw heartbeat messages into the engine
- instead introduce a thin adapter that converts confirmed master assignment state into:
- stable
ReplicaID - endpoint set
- epoch
- recovery target kind
- stable
That keeps master as control owner and the engine as execution owner.
Important note:
- the adapter should treat heartbeat as the transport carrier, not as the final protocol shape
- block-control semantics should be made explicit over that carrier
- if a later phase concludes that heartbeat-only carriage is too coarse, that should be a separate design decision after the real hardening path is measured
2. StorageAdapter
Engine interface today:
GetRetainedHistory()PinSnapshot(lsn)/ReleaseSnapshot(pin)PinWALRetention(startLSN)/ReleaseWALRetention(pin)PinFullBase(committedLSN)/ReleaseFullBase(pin)
Real mapping should be:
- retained history source:
- current WAL head/tail/checkpoint state from
weed/storage/blockvol/blockvol.go - recovery helpers in
weed/storage/blockvol/recovery.go
- current WAL head/tail/checkpoint state from
- WAL retention pin:
- existing retention-floor / replica-aware WAL retention machinery around
shipperGroup
- existing retention-floor / replica-aware WAL retention machinery around
- snapshot pin:
- existing snapshot/checkpoint artifacts in
blockvol
- existing snapshot/checkpoint artifacts in
- full-base pin:
- explicit pinned full-extent export or equivalent consistent base handle from
blockvol
- explicit pinned full-extent export or equivalent consistent base handle from
Important constraint:
Phase 07must not fake this by reconstructingRetainedHistoryfrom tests or metadata alone
3. Execution Driver / Executor hookup
Engine side already has:
- planner/executor split in
sw-block/engine/replication/driver.go - stepwise executors in
sw-block/engine/replication/executor.go
Real mapping should be:
- engine planner decides:
- zero-gap / catch-up / rebuild
- trusted-base requirement
- replayable-tail requirement
- blockvol runtime performs:
- actual WAL catch-up transport
- actual snapshot/base transfer
- actual truncation / apply operations
Recommended split:
- engine owns contract and state transitions
- blockvol adapter owns concrete I/O work
First-Slice Acceptance Rule
For the first integration slice, this is a hard rule:
blockvolmay execute recovery I/Oblockvolmust not own recovery policy
Concretely, blockvol must not decide:
- zero-gap vs catch-up vs rebuild
- trusted-base validity
- replayable-tail sufficiency
- whether rebuild fallback is required
Those decisions must remain in the V2 engine.
The bridge may translate engine decisions into concrete blockvol actions, but it must not re-decide recovery policy underneath the engine.
First Product Path
The first product path should be:
RF=2block volume replication on the existing heartbeat/assignment loop- primary + one replica
- failover / reconnect / changed-address handling
- rebuild as the formal non-catch-up recovery path
This is the right first path because it exercises the core correctness boundary without introducing N-replica coordination complexity too early.
What Must Be Replaced First
Current engine-stage pieces that are still mock/test-only or too abstract:
Replace first
mockStoragein engine tests
- replace with a real
blockvol-backedStorageAdapter
- synthetic control events in engine tests
- replace with assignment-driven events from the real master/volume-server path
- convenience recovery completion wrappers
- keep them test-only
- real integration should use planner + executor + storage work loop
Can remain temporarily abstract in Phase 07 P0/P1
ControlPlaneAdapterexact public shape
- can remain thin while the integration path is being chosen
- async production scheduler details
- executor can still be driven by a service loop before full background-task architecture is finalized
Recommended Concrete Modules
Engine stays here
sw-block/engine/replication/
First real adapter package should be added near blockvol
Recommended initial location:
weed/storage/blockvol/v2bridge/
Reason:
- keeps V2 engine independent under
sw-block/ - keeps real-system glue close to blockvol storage truth
- avoids copying engine logic into
weed/
Suggested contents:
control_adapter.go
- convert master assignment / local apply path into engine intents
storage_adapter.go
- expose retained history, pin/release, trusted-base export handles from real blockvol state
executor_bridge.go
- translate engine executor steps into actual blockvol recovery actions
observe_adapter.go
- map engine status/logs into service-visible diagnostics
First Failure Replay Set For Phase 07
The first real-system replay set should be:
- changed-address restart
- current risk: old identity/address coupling reappears in service glue
- stale epoch / stale result after failover
- current risk: master and engine disagree on authority timing
- unreplayable-tail rebuild fallback
- current risk: service glue over-trusts checkpoint/base availability
- plan/execution cleanup after resource failure
- current risk: blockvol-side resource failures leave engine or service state dangling
- primary failover to replica with rebuild pending on old primary reconnect
- current risk: old V1/V1.5 semantics leak back into reconnect handling
Non-Goals For This Slice
Do not use Phase 07 to:
- widen catch-up semantics
- add smart rebuild optimizations
- redesign all blockvol internals
- replace the full V1 runtime in one move
- claim production readiness
Deliverables For Phase 07 P0
A good P0 delivery should include:
- chosen service slice
- chosen integration path in the current repo
- adapter-to-module mapping
- list of test-only adapters to replace first
- first failure replay set
- explicit note of what remains outside this first slice
Short Form
Phase 07 P0 should start with:
- engine in
sw-block/engine/replication/ - bridge in
weed/storage/blockvol/v2bridge/ - first real slice = blockvol primary + one replica on the existing master heartbeat / assignment path
ReplicaID = <volume-name>/<replica-server-id>for the first sliceblockvolexecutes I/O but does not own recovery policy- first product path =
RF=2failover/reconnect/rebuild correctness