Commit Graph
13249 Commits
Author SHA1 Message Date
pingqiuandClaude Opus 4.7 38cb25702f T4 kick-off: L1 V2 replication entity survey (pre-sketch review)
First artifact for T4 (Gate G5 Replicated Write Path) under the §8C.8
top-down port discipline added post-T3 retrospective. This is L1 only
— raw V2 entity enumeration with scope / lifecycle / concurrency /
cross-session / authority / protocol / invariants attributes. No V3
bridge verdicts proposed yet; L2 follows only after L1 review closes.

9 entities identified across replication surface:
- WALShipper (per-replica fan-out)
- ShipperGroup (per-volume aggregator)
- ReplicaReceiver (per-volume replica-side listener)
- ReplicaBarrier FSM (per-barrier ephemeral)
- DistGroupCommit closure (per-write-op, mode-aware)
- RebuildSession (volatile, non-crash-durable)
- RebuildServer (per-primary listener)
- RebuildTransportServer / Client (per-session base lane)

9 L1-level observations flagged as L2 hazards: epoch fencing
pervasiveness, contiguous-LSN cross-cutting invariant, two-lane
rebuild bitmap integration, mode-dependent durability, volatility
of rebuild session (vs BUG-005 Provider cache lesson), explicit
reconnect protocol, three-phase barrier, ioMu.RLock nesting,
shipper-group double watermark.

5 open questions raised for sw / architect / PM review before L1
sign: scope completeness (sync_all_reconnect, split-brain arbiter?),
scope accuracy (ReplicaReceiver per-volume vs per-assignment),
RebuildSession volatility confirmation, DistGroupCommit V3 residence
opinion, protocol-frame wire-compat stance.

Status: DRAFT — open for sw review; L2 + L3 work blocked on L1 sign
per §8C.8 discipline.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 22:30:57 -07:00
pingqiuandClaude Opus 4.7 88dcd49d67 sw-block/design: T3 mini-plan + audit + sketch docs (pre-close docs)
Predecessor docs for the T3 batch, retained here for audit trail.
The closure report (`v3-phase-15-t3-closure-report.md`), contract
bridge catalogue, and BUG-005/006 artifacts already landed in
commits `4127e5136` + `6e196885e`; this commit fills the docs
those closure artifacts reference back to.

Landed:
  v3-phase-15-t3-port-plan-sketch.md    T3 umbrella sketch (rev-2.1, three-signed)
  v3-phase-15-t3-port-audit.md          T3.0 port audit + Addendum A (QA-signed)
  v3-phase-15-t3a-mini-plan.md          T3a scope + sign-off (CLOSED 0e1595c)
  v3-phase-15-t3b-mini-plan.md          T3b scope + sign-off (CLOSED 72d0d40)
  v3-phase-15-t3c-mini-plan.md          T3c scope + sign-off (CLOSED 829c6a9)

Total 1,346 lines of doc; no code impact.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 22:19:03 -07:00
pingqiuandClaude Opus 4.7 6e196885e4 T3 closure: reconcile §8C.3 trigger narrative + C5 pin strength
Two document-truthfulness mismatches flagged by architect review:

§B Governance transition (closure report): previously claimed "no
§8C.3 triggers fired during T3"; §H Phase 3's own BUG-005 description
matches trigger #1 (unknown-unknown architectural bug, V2/V3 shape-
level mismatch). Corrected to say trigger #1 fired once (BUG-005)
and was handled per §8C.3, with log entry, architect+PM notification,
catalogue §2.3 drift-event row, and porting-discipline citation.

C5-NVME-SESSION-STATE-CLEANUP-ON-CLOSE (contract bridge catalogue
§2.2.14): previously stated "PASSES today" / "pinned explicitly".
Closure §H Phase 4 correctly narrows landed tests to "smoke +
goroutine-leak guard" with Target.ctrls/AER/KATO-stored-ms
introspection not exercised. Catalogue row now matches that
strength: "pin strength today: smoke + goroutine-leak guard only;
full state-release introspection NOT exercised; queued as
follow-up".

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 22:14:08 -07:00
pingqiuandClaude Opus 4.7 4127e5136c T3 closure: finalize sign-ready state + BUG-006/007 + catalogue retrofill
Closure report (v3-phase-15-t3-closure-report.md):
- §E rewritten as FINAL A–F m01 verification table with per-impl
  status + G4 pass criterion = smartwal (production default) full
  A–F green; walstore non-default fallback with Matrix D failure
  tracked via BUG-007
- §E sign table: QA re-sign 2026-04-22 with evidence basis
  (seaweed_block@313dd52 + BUG-005 fix 42b045a); prior RETRACTED
  row superseded
- §D INV-DURABLE-001: conditional "Path B pending" wording
  removed; scoped to smartwal; canonical row name stands
- §B non-claims: stale _TBD_ perf wording replaced with
  first-light scope statement; new non-claim "G4 pass =
  smartwal only; walstore deferred via BUG-007" added
- §G.3 finalized: FINAL resolution with smartwal A–F PASS;
  walstore deferred
- §H Phase 2 narrative updated to match final matrix outcome
  (Matrix E smartwal-only; walstore E skipped pending BUG-007)
- §H Phase 4: T3-DEF-6 test wording downgraded from
  "pins cleanup contract" to "smoke + goroutine-leak guard"
  per PM feedback (no test-only introspection of Target.ctrls/
  AER/KATO internals; follow-up deferred)
- §H Phase 5: BUG-007 filed and scoped; non-blocking basis
  spelled out

Contract Bridge Catalogue (v2-v3-contract-bridge-catalogue.md):
- §2.2.14 C1-NVME-SESSION-KATO reclassified PRESERVE-partial
  → VIOLATED with BUG-006 anchor + m01 Matrix D evidence
- §2.2.14 C5-NVME-SESSION-STATE-CLEANUP-ON-CLOSE added
  (T3-DEF-6 retrofit, pinned by QA L1 addendum)
- §2.3 drift-event audit table expanded with BUG-006, BUG-007,
  T3-DEF-5, T3-DEF-6

BUG-006 (006_nvme_kato_timer_not_enforced.md):
- Unified contract ID to catalogue name
  C1-NVME-SESSION-KATO-STORED-NOT-ENFORCED (was drifting as
  C3-NVME-KATO-ENFORCEMENT, PM Low catch)
- §7 reframed as "existing row reclassified VIOLATED"
  rather than "add new row"

BUG-007 (007_walstore_umount_remount_data_loss.md): filed as
pre-existing walstore-specific durability bug surfaced by
Matrix D re-verify; explicitly non-blocking for T3 since
smartwal is production default.

BUG-005 (005_backend_close_cross_session.md): committed for
HEAD-reproducibility (referenced by closure §H Phase 3).

Inventory (bugs/inventory/nvme-test-coverage-deferred.md):
T3-DEF-5/6/7 struck through with per-row resolution pointers;
zero open T3-scope inventory rows remaining.

Evidence artifacts committed in seaweed_block@313dd52
(scripts/iterate-m01-nvme.sh Matrix F robustness +
t3_qa_session_cleanup_addendum_test.go).

Awaiting architect + PM three-sign.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 22:08:30 -07:00
pingqiuandClaude Opus 4.7 953fdb7564 doc: P14 S8 final bounded close — evidence matrix + P15 handoff
Adds the six S8 closure deliverables consolidating S4-S7 evidence,
classifying V2 scenarios, and mapping residual product gaps onto
canonical P15 tracks (per v3-phase-15-product-plan.md §4).

New docs:
- v3-phase-14-s8-assignment.md — S8 execution contract.
- v3-phase-14-s8-final-bounded-close.md — bounded P14 target,
  accepted topology, reject conditions.
- v3-phase-14-s8-evidence-matrix.md — 16 claims × {L0, L1, L2, L3,
  Status, Residual}. 15 PROVEN, 1 PARTIAL (Claim 15 fence
  quantitative bound, P14 internal follow-up). Rounds 2-3 architect
  corrections: Claim 10 / 12 L2 narrowed; Claim 6 refresh gap closed
  by the new L1 test (see companion commit in seaweed_block).
- v3-phase-14-s8-v2-scenario-classification.md — every V2 scenario
  mapped to RUNNABLE-P14 / BLOCKED-FRONTEND / BLOCKED-OPS /
  BLOCKED-HA / BLOCKED-PERF / PORT-MECHANISM; scenario YAMLs kept
  as L3 shape, not executed evidence.
- v3-phase-14-s8-p15-handoff.md — 11 rows (10 canonical P15 tracks
  + 1 P14 internal follow-up anchored to Claim 15 PARTIAL); §4
  integrity check split by row class.
- v3-phase-14-s8-closure.md — final P14 closure statement matching
  the close doc §10 wording; explicit non-goals; all 9 P15 tracks
  named with canonical numbering.

No claim of CSI / frontend / migration / security / performance /
production readiness. Every product gap is handed off with a
concrete first-proof gate.

Companion: seaweed_block commit adds the IntentRefreshEndpoint L1
route test that closes Claim 6.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 01:44:11 -07:00
pingqiuandClaude Opus 4.6 247d9f6fa6 doc: V3 observability — structured logging, tracing, metrics, debug zip, alerts
Covers 6 areas based on CockroachDB/Ceph/etcd/Longhorn research:

1. Structured logging: zap + JSON + channel model (OPS/STORAGE/REPL/ISCSI/AUDIT/HEALTH)
2. Distributed tracing: OpenTelemetry spans across write/rebuild/failover paths
3. Metrics: 40+ must-have Prometheus metrics with histogram latency buckets
4. Debug tools: debug zip (logs+pprof+state), log merge, live tail
5. Audit logging: every admin mutation with actor/target/operation/result
6. Alert design: 3 tiers (page/ticket/log), anti-patterns to avoid

Identifies existing gaps: no I/O latency histogram, no rebuild duration
metric, no audit trail, no structured logging, no distributed tracing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 02:32:30 -07:00
pingqiuandClaude Opus 4.6 9437bd0b95 doc: V3 development process — branch strategy, CI/CD, review, release
Covers full engineering process based on SeaweedFS upstream audit:
- Branch strategy: feature/sw-block with checkpoint branches for perf baselines
- Commit conventions: type: description format
- Code review checklist with anti-pattern checks
- Testing standards: 5 levels, 1600+ tests, 4 hardware acceptance scenarios
- CI/CD pipeline: unit→component→hardware gates
- Release process: checklist, artifacts, versioning
- Issue/PR templates with anti-pattern classification
- Agent collaboration model (architect/sw/tester/manager roles)
- Code quality: golangci-lint config, race detection
- Upstream contribution path for SeaweedFS merger

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 01:58:19 -07:00
pingqiuandClaude Opus 4.6 f11a5829d7 doc: update operations design — add existing V1 UI foundation, code map
Added section 8: existing UI/admin infrastructure from V1:
- iSCSI admin HTTP server (admin.go: /status, /assign, /rebuild, /snapshot)
- Grafana dashboard JSON (block-overview.json, already built)
- Master UI HTML (master.html, add Block Volumes tab)
- Volume server UI HTML (volume.html, add Block section)
- Prometheus metrics (already integrated)

Added section 10: existing vs new code map showing most backend
exists — work is wiring to user-facing interfaces.

Updated Phase 1 to include Master UI tab (+200 lines HTML/JS).
Updated Phase 5 with two options (lightweight extend vs full SPA).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 01:44:47 -07:00
pingqiuandClaude Opus 4.6 3ba622d9e0 doc: V3 operations design — user-friendly setup, shell commands, REST API
Covers three personas (developer/operator/platform engineer) with:
- One-command setup: weed server -block (10 seconds to first volume)
- Shell commands: block.list, block.status, block.health, block.create, etc.
- REST API: /block/volumes CRUD, /block/health
- Observability: Prometheus metrics, alerting rules, Grafana dashboard
- Actionable error messages (every error tells you what to do next)
- Dry-run by default for all destructive operations

Competitive comparison: 10s setup vs Ceph 30min, 13.5x write IOPS,
single binary for object + block storage.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 00:36:32 -07:00
pingqiuandClaude Opus 4.6 2bc8dfcdde doc: update testrunner roadmap — add runs.db text index for result tracking
P1 feature updated: replace generic "structured results" with concrete
runs.db design (newline-delimited JSON, one line per run). Leverages
existing RunBundle system (manifest.json, result.json already exist).

New CLI commands: list, trend, gc, reindex, diff.
Regression detection via stddev comparison against rolling baseline.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 22:00:35 -07:00
pingqiuandClaude Opus 4.6 676539d3b9 doc: testrunner roadmap + dm-stripe scenario (42/42 PASS, 1.87x write IOPS)
testrunner-roadmap.md: P0-P3 feature plan for multi-version comparison,
Ceph adapter, result tracking, cluster templates, debug mode.

dm-stripe-two-server.yaml: proven Linux dm-stripe across 2 sw-block
volumes on 2 servers. Results: single=42K IOPS → striped=79K IOPS (1.87x).
Data integrity verified via md5. Zero sw-block code changes needed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 21:57:03 -07:00
pingqiuandClaude Opus 4.6 25ede892b4 doc: external failure taxonomy — 20 real bugs from Ceph/DRBD/Mayastor/Longhorn
Catalogs production failures organized by semantic class:
- Membership/liveness misjudgment (4 cases)
- Recovery decision error (3 cases)
- Completion/durability illusion (4 cases)
- Ordering/race conditions (4 cases)
- Background work corrupts semantics (3 cases)

Each entry maps to V2 exposure and V3 prevention rules.
Includes "Would V2 Have This Bug?" self-audit checklist.

Sources: Ceph tracker, DRBD changelogs, Longhorn/Mayastor GitHub issues.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 01:21:08 -07:00
pingqiuandClaude Opus 4.6 8ecc506452 V2 stabilization: 144/144 hardware actions PASS + design docs + SmartWAL prototype
Hardware scenarios (all PASS on m01/m02, 25Gbps RoCE):
- I-V3 auto-failover: 43/43 (create→write→kill→promote→verify IO)
- I-R8 rebuild-rejoin: 58/58 (failover→write→restart→1GB rebuild in 2s→verify data)
- Fast rejoin: 43/43 (kill replica→3s→restart→recovery→data verified)

Performance: V2 RF=1 = 46,666 IOPS vs V1.5 RF=1 = 47,233 IOPS (-1.2%, noise)

New test scenarios:
- v2-rebuild-rejoin.yaml: full failover→rebuild→second failover→data integrity
- v2-fast-rejoin-catchup.yaml: replica kill→fast restart→recovery
- v2-rebuild-failure-retry.yaml: kill during rebuild→restart→data verified
- rf1-perf-compare.yaml: RF=1 perf baseline for V1.5 vs V2 comparison

Design documents:
- protocol-anti-patterns.md: 7 anti-patterns with cases from SeaweedFS/Ceph/DRBD
- smartwal-design-memo.md: extent-first write algorithm research (BlueStore/ZFS/DRBD)
- smartwal-prototype-spec.md: prototype spec with 16/16 crash tests PASS
- v3-clean-recovery-draft.md: V3 semantic cleanup principles
- v2-integration-matrix.md: 25-row integration coverage map
- v2-acceptance-evidence.md: gap analysis for remaining work

SmartWAL prototype (16/16 tests PASS):
- smartwal.go, smartwal_record.go, smartwal_recovery.go: core implementation
- smartwal_test.go: 9 single-node crash tests
- smartwal_repl_test.go: 7 two-node replication crash tests

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 00:18:20 -07:00
pingqiuandClaude Opus 4.6 5279bd3945 fix: tolerate missing sender in remote rebuild ack observation
The architect's refactor correctly routes remote rebuild acks through
the shared observation path (pins, watchdog, deferred terminal success).
But requireReplicaSession fails with "sender not found" when the
orchestrator registry is reconciled between installSession and the
first ack arrival.

Fix: when emitTerminal=false (remote path), treat sender-not-found as
non-fatal. The remote coordinator already validated the session — the
sender lookup is for local observation only. Pins and watchdog handle
nil snap gracefully (updateRebuildProgressPin line 296 already checks
snap != nil).

This preserves the architect's design (shared observation + deferred
terminal success) while tolerating the sender registry race that only
affects the remote rebuild path.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 23:00:06 -07:00
pingqiuandClaude Opus 4.6 008ea03ef5 fix: suppress SessionFailed after successful remote rebuild completion
After RemoteRebuildIO.TransferFullBase returns, the OnAck callback has
already emitted SessionCompleted and stored achievedLSN. But
RebuildExecutor.Execute() continues calling sender methods which fail
("sender stopped") because the completion event already cleaned up the
sender. This error propagated to ExecutePendingRebuild which emitted a
spurious SessionFailed, knocking the mode back to degraded.

Fix: check remoteRebuildAchieved before emitting SessionFailed. If the
rebuild already completed via the ack path, log the post-completion
error but suppress the SessionFailed event.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 17:32:37 -07:00
pingqiuandClaude Opus 4.6 55862f1ab1 fix: rebuild base-only completion + protocol handshake + direct ack events
Three fixes for the remote rebuild path:

1. Base-only completion: when BaseLSN == TargetLSN, the base image covers
   all data — no WAL tail needed. MarkBaseComplete now auto-satisfies the
   WAL condition and calls TryComplete so the session completes immediately
   after the base transfer finishes.

2. Base lane protocol handshake: runBaseLaneClient now sends MsgRebuildReq
   {Type: RebuildSessionBase} before reading. The RebuildServer requires
   this handshake to dispatch to ServeBaseBlocks. Without it, the server
   received raw frames it couldn't understand.

3. Direct ack events: OnAck emits engine events directly (SessionCompleted,
   SessionProgressObserved, SessionFailed) instead of routing through
   ObserveReplicaRebuildSessionAck which requires the sender in the
   orchestrator registry. The remote coordinator owns the session — no
   registry lookup needed.

Also adds diagnostic logging on both sides:
- Replica: logs parsed RebuildAddr and base lane client start
- Primary: logs sender state after installSession

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 16:18:44 -07:00
pingqiuandClaude Opus 4.6 0faf93a152 diag: add sender registry verification after installSession
The accepted ack from the replica is rejected with "sender not found"
even though installSession succeeds. Add diagnostic logging to verify
the sender exists in the orchestrator registry immediately after
installSession, and dump all registry IDs if not found.

This will reveal whether the sender is removed between installSession
and the ack arrival (by syncProtocolExecutionState, evaluateActivationGate,
or another ProcessAssignment that reconciles with a stale replica list).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 15:53:33 -07:00
pingqiuandClaude Opus 4.6 943000ae8e fix: RebuildSourceDecision returns FullBase when CommittedLSN=0
When CommittedLSN=0 (sync_all mode, replica degraded), snapshot-tail
rebuild was chosen because IsRecoverable(checkpoint, 0) is vacuously
true (0 <= HeadLSN always). But snapshot-tail requires a valid committed
endpoint for tail-replay. Without it, ExecuteRebuildPlan calls
TransferSnapshot which RemoteRebuildIO doesn't support → immediate fail.

Fix: if CommittedLSN=0, force RebuildFullBase. This is the correct
source when the primary has data but no replica has confirmed durability.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 15:46:00 -07:00
pingqiuandClaude Opus 4.6 a79cba0be7 fix: PlanRebuild targetLSN=0 when replica is degraded (CommittedLSN fallback)
Root cause: StatusSnapshot().CommittedLSN reports 0 in sync_all mode when
the replica shipper has no flushed progress (NeedsRebuild state). This is
correct for lineage-safe committed boundary, but PlanRebuild uses
CommittedLSN as RebuildTargetLSN. With target=0, shouldStartSessionCommand
rejects the StartRebuildCommand, and the rebuild IO never executes.

Fix: PlanRebuild falls back to HeadLSN when CommittedLSN is 0. The
primary's WAL head IS the data boundary the replica needs to reach.
The fact that no replica has confirmed durability is exactly why we're
rebuilding.

Also adds command type logging to coreApplyAndLog so tester can verify
which commands are actually emitted vs silently dropped.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 15:35:31 -07:00
pingqiuandClaude Opus 4.6 bc767eb9d2 fix: rebuild correctness — single completion, fail-closed acks, diagnostic logging
Three correctness fixes for the remote rebuild path:

1. No double completion: for remote rebuilds, OnRebuildCompleted skips
   RebuildCommitted since ObserveReplicaRebuildSessionAck already emitted
   SessionCompleted on the accepted ack. One rebuild = one completion event.

2. SessionAckFailed with rejected observation: if OnAck rejects the failed
   ack (stale session), don't use the sentinel errRebuildAckFailed. Return
   a regular error so ExecutePendingRebuild emits the fallback SessionFailed.
   No path leaves the engine session hanging.

3. Diagnostic logging in ExecutePendingRebuild: log the replicaID and
   targetLSN on both nil-return (TakeRebuild mismatch) and successful take
   paths. Also log the pending store in runRebuild with replicaID, targetLSN,
   and IO type. This makes the TakeRebuild seam diagnosable on hardware
   without rebuilding the engine package.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 15:25:26 -07:00
pingqiuandClaude Opus 4.6 df69c83f41 feat: RemoteRebuildIO — primary coordinates rebuild, replica installs
Replace the broken primary-local rebuild executor with RemoteRebuildIO,
a server-side engine.RebuildIO implementation that coordinates remotely.
The primary sends SessionControlV2 (with RebuildAddr trailer) to the
replica's control channel; the replica starts a local rebuild session
and auto-connects to the primary's rebuild server for the base lane.

Single rebuild route: ALL core-present rebuilds use RemoteRebuildIO.
The entire command chain is preserved unchanged:
  PlanRebuild → pending → RebuildStarted → StartRebuildCommand
  → ExecutePendingRebuild → RemoteRebuildIO.TransferFullBase

Key changes:
- SessionControlMsg v2: optional RebuildAddr trailer (len-based decode)
- ReplicaRebuilding shipper state: session-gated live WAL lane
- RemoteRebuildIO: dials replica ctrl, sends session control, reads acks
- Ack forwarding through ObserveReplicaRebuildSessionAck (pins/watchdog)
- Completion proof from replica's achievedLSN, not primary's local vol
- Transport failures emit SessionFailed (no double-emit on ack failures)
- Progress ack rejection fails closed (stale session = abort)
- Replica auto-starts base lane client on v2 session control

State transitions:
  NeedsRebuild → [accepted ack] → Rebuilding → [completed] → InSync
  Rebuilding → [failed/EOF] → NeedsRebuild → [next probe] → retry

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 15:04:22 -07:00
pingqiuandClaude Opus 4.6 befe049b09 refactor: unified primary onboarding + rebuild execution wiring
Replace three bypass mechanisms with one unified model. When the
probe returns ProbeRebuildRequired, the host now starts the rebuild
through the existing recovery manager (StartRecoveryTask), which
resolves the rebuild address, plans the rebuild, and executes via
the v2bridge executor — the same path as master-driven RoleRebuilding.

New per-replica probe API:
- WALShipper.ProbeReconnect() → ReplicaProbeResult with typed outcome
- ShipperGroup.ProbeReconnectAll() → []ReplicaProbeResult
- BlockVol.ProbeReplicaOnboarding() / IsClosed()

Host-side wiring:
- handleReplicaProbeResult routes outcomes:
  KeepUp → ShipperConnectedObserved
  CatchUp → ShipperConnectedObserved (recovery manager handles session)
  Rebuild → NeedsRebuildObserved + StartRecoveryTask (executes rebuild)
  TemporaryFailure → no-op
- lastAssignmentsForPath reconstructs assignment for recovery manager
- onPrimaryRosterChanged probes all replicas (defined, called from watchdog)
- observePrimaryShipperConnectivity uses probe API

Probe fires via syncProtocolExecutionState immediately after assignment
processing — same heartbeat cycle, no timer delay.

Deleted: startDirectRebuild, resolveCtrlAddrForShipper,
TryReconnect/TryReconnectAll/TryReconnectShippers.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 02:33:07 -07:00
pingqiuandClaude Opus 4.6 d6bc7516f1 feat: primary-direct rebuild — start rebuild session on NeedsRebuild
When proactive reconnect finds WAL gap exceeds retained range:
1. Emit per-replica NeedsRebuildObserved to engine (with ReplicaID)
2. Resolve replica ctrl address from shipper group
3. Start direct rebuild session: send sessionControl(start_rebuild)
   to replica's ctrl channel, stream base blocks, emit RebuildStarted

The primary drives the rebuild directly without master round-trip.
The master sees the result via heartbeat projection (needs_rebuild →
rebuilding → healthy). This matches V2 authority: master owns identity,
primary owns data-control recovery.

Added WALShipper.CtrlAddr() getter for address resolution.
resolveCtrlAddrForShipper maps data address to ctrl address via
shipper group (works for RF=2 and RF=3+).

startDirectRebuild runs in a goroutine: dials replica ctrl, sends
start_rebuild, waits for accepted ack, serves base blocks, emits
RebuildStarted to engine on success.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 01:04:00 -07:00
pingqiuandClaude Opus 4.6 8b469cf70b fix: revert Bridge 2, fix Bridge 1 with per-replica identity
Revert detectAndEnqueueRebuildFromHeartbeat (Bridge 2) — master
should not drive rebuild assignments from heartbeat. The primary
owns data-control recovery per the V2 authority split.

Fix Bridge 1: NeedsRebuildObserved now carries per-replica identity.
resolveReplicaIDForShipper maps shipper DataAddr to ReplicaID via
the shipper group (works for RF=2 and RF=3+). The engine receives
the specific replica that needs rebuild, not a volume-level broadcast.

Primary-direct rebuild: the primary detects which replica needs
rebuild and will drive the session directly. The master learns about
it via subsequent heartbeat projection (needs_rebuild → rebuilding →
healthy). No master round-trip needed for the rebuild decision.

Added WALShipper.DataAddr() getter for address resolution.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 00:55:50 -07:00
pingqiuandClaude Opus 4.6 f90ccf5bfd fix: proactive shipper reconnect on rejoin (Bug 5)
After rejoin, the shipper is configured but no I/O triggers Ship(),
so the shipper stays Disconnected and the core stays at
awaiting_shipper_connected indefinitely.

Fix: observePrimaryShipperConnectivity now calls TryReconnectShippers
when ShipperConfigured=true but ShipperConnected=false. This triggers
the full reconnect protocol (dial + handshake + bounded catch-up)
proactively, bringing the replica current without waiting for I/O.

Option B approach: uses the same reconnect path as Barrier() — not a
fake write or bare dial probe. CatchUpTo(headLSN) replays any retained
WAL entries, bringing the replica fully current.

New methods:
- WALShipper.TryReconnect(): full reconnect without foreground I/O
- ShipperGroup.TryReconnectAll(): probes all disconnected shippers
- BlockVol.TryReconnectShippers(): volume-level entry point

Also fix pre-existing test expectation: engine now emits
start_recovery_task on primary assignment with replicas.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 00:14:46 -07:00
pingqiuandClaude Opus 4.6 53246d2780 fix: recover TOCTOU + WAL pressure edge case tests
Fix recover path TOCTOU: re-Lookup after AddReplica so the primary
refresh assignment includes the freshly added replica addresses.
Previously, Lookup (copy) was called before AddReplica modified the
registry, so entry.Replicas was empty → primary got replicas=0 →
shipper never configured.

Add 2 WAL pressure edge case tests:
- ShipperCatchUpOrEscalate: 64KB WAL, 200 writes, aggressive flusher.
  Proves no hang/deadlock/corruption. Shipper either keeps up or
  correctly escalates to NeedsRebuild.
- RebuildWithPinWhilePrimaryWrites: rebuild session active while
  primary writes 7600+ blocks in 2s. Proves primary never freezes
  — rebuild pin is on replica only, primary WAL recycles freely.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 23:56:26 -07:00
pingqiuandClaude Opus 4.6 e0116fc631 fix: three hardware blockers — WAL retention + registry race + shutdown beat
All 43 actions pass on m01/m02 hardware. Auto-failover PASS.
dd_write: 30s → 123ms. Post-failover write: 33,621 IOPS.

1. WAL retention: remove keepup retention floor (MinShippedLSN).
   WAL cannot be pinned during sustained async writes — any pin
   strategy either fills WAL (blocking writes) or over-recycles
   (breaking catch-up). Flusher recycles freely. Future LBA map
   will provide catch-up without WAL retention.
   MinShippedLSN on ShipperGroup retained as diagnostic surface.

2. Registry stale-cleanup race: add RegisteredAt grace period.
   Race: master registers volume → next VS heartbeat arrives before
   VS discovers the volume → stale cleanup deletes the entry →
   failover finds 0 entries. Fix: skip stale cleanup for entries
   registered within 30s (> 2 heartbeat intervals).
   2 new tests: grace protects new entry, old entry still cleaned.

3. Shutdown heartbeat: VS disconnect heartbeat no longer claims
   block inventory authority. Previously, the shutdown beat's
   empty inventory triggered stale cleanup, deleting the entry
   before failover could use it.

Scenario fix: recovery-baseline-failover.yaml now kills the
correct node (discovered primary, not hardcoded), connects to
the correct new primary for post-failover verification.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 22:59:46 -07:00
pingqiuandClaude Opus 4.6 39f1232fe2 feat: validation matrix closure — Rebuild Ready 12/12, Restore Ready 10/10
Close all Rebuild Ready and Restore Ready matrix gaps. V2 Ready at 10/14
(2 partial, 2 missing — honest assessment).

New tests (tester-written):
- R1: syncAck-driven trigger via protocol engine decision
- R3: stale replica restart beyond WAL → rebuild converges
- R5: connection drop mid-base → cancel → fresh rebuild converges
- R10: failover-rejoin with forced WAL recycling, strict rebuild assert
- R11: divergent replica full overwrite convergence
- R12: crash mid-rebuild → fresh session converges (not resume)
- S2: corrupt WAL entry + corrupt base block both rejected
- S5: snapshot-tail rebuild (base + WAL tail replay)
- S7: crash between base install and tail replay
- S8: snapshot under concurrent writes
- V5: rebuild complete without DurableLSN blocks publish_healthy
- V9: mixed replica health aggregate projection
- V14: negative fail-closed matrix (epoch, kind, stale)

Bug fix: StartRebuildSession now clears stale dirty map + resets WAL +
updates checkpoint AFTER safety check but BEFORE session.Start(). Fixes
stale extent data shadowing rebuild base blocks on reopened replicas.

Cleanup: remove 14 obsolete design docs (migration batches, old WAL-v2
specs, simulator goals) — all superseded by current protocol docs.

34 component tests + 8 protocol engine tests + server tests all pass.
1GB CRC validation passes in 19s.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 16:31:55 -07:00
pingqiuandClaude Opus 4.6 59a36013d4 feat: rebuild hardening A1-A5 + session-controlled execution path
A1 Engine kind-routing fix:
  SessionProgressObserved/Completed/Failed now respect active session
  Kind. Rebuild progress no longer leaks into catch-up aggregate.
  sessionKindMismatch guard + observeRebuildProgress helper.
  2 regression tests lock kind isolation.

A2 Retention pin:
  Rebuild session ack drives progress-based WAL retention floor.
  Pin installed at base_lsn on accepted, advances with wal_applied_lsn,
  released on completed/failed/cancelled. rebuildProgressPinFloor
  returns min across all active replicas.
  Retention pin test: 100 blocks fill WAL, 5 flusher cycles with
  20 pinned rebuild entries — all verified correct.

A3 Progress ack emission:
  Automatic sessionAck(running/base_complete/completed/failed) emitted
  from rebuild session lifecycle transitions. sessionAckLocked builds
  ack under session lock. emitRebuildSessionAck callback wired through
  SetOnRebuildSessionAck on BlockVol.
  ObserveReplicaRebuildSessionAck maps acks to core engine events.
  WireLocalReplicaRebuildSessionAcks bridges local callback to server.
  5 server tests proving ack→core, pin advance, pin cleanup.

A4 Deadline/timeout:
  rebuildAckWatch watchdog: armed on accepted/running/base_complete,
  refreshed on each ack, cleared on completed/failed. Timeout
  cancels local session + clears pin + fail-closes.
  2 tests: timeout→fail-close, progress→refresh.

A5 Session-controlled execution path:
  v2bridge.Executor.TransferFullBase now uses session-controlled loop:
  beginControlledFullBase → real sessionControl over TCP →
  transferExtentToSession via RebuildTransportClient →
  PrepareFullBaseRebuild → TryCompleteRebuildSession.
  ReplicaReceiver control channel handles MsgSessionControl alongside
  MsgBarrierReq. Session acks written back on same TCP connection.
  RebuildSessionBase request type separates new per-block stream from
  legacy raw extent stream. Full-base cleanup deferred until success.
  Deadlock fix: ApplyBaseBlock releases session lock before ioMu.
  Hydration skip for full-base sessions.

23 rebuild component tests (all pass):
  11 kernel correctness, 8 transport/runtime, 3 scenario-scale,
  including 1GB primary-initiated with CRC validation.

29 files changed, ~2500 insertions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 14:39:11 -07:00
pingqiuandClaude Opus 4.6 342f8baa69 feat: rebuild transport wiring — session control + base block streaming
Wire protocol messages and transport handlers for the rebuild MVP:

Protocol messages (rebuild_transport.go):
- SessionControlMsg: epoch, sessionID, command, baseLSN, targetLSN,
  snapshotID. Encode/Decode with fixed 37-byte wire format.
- SessionAckMsg: epoch, sessionID, phase, walAppliedLSN, baseComplete,
  achievedLSN. Encode/Decode with fixed 34-byte wire format.
- MsgSessionControl (0x10) and MsgSessionAck (0x11) on control channel.
- SendSessionControl/SendSessionAck convenience functions.

Transport handlers:
- RebuildTransportServer: primary-side, streams all extent blocks as
  MsgRebuildExtent frames (reusing existing rebuild message type),
  ends with MsgRebuildDone.
- RebuildTransportClient: replica-side, receives base blocks and
  routes through vol.ApplyRebuildSessionBaseBlock, marks base
  complete on MsgRebuildDone.

4 transport tests:
- SessionControl wire round-trip
- SessionAck wire round-trip
- BaseBlockStreaming: full TCP loop, 1024 blocks streamed and verified
- SessionControlOverTCP: real TCP send/receive with accepted ack

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 14:57:43 -07:00
pingqiuandClaude Opus 4.6 49845dd509 feat: server-layer rebuild session skeleton — host routing for MVP
Add BlockService replica-side rebuild routing API that bridges
transport/host layer to BlockVol session surface:

  StartReplicaRebuildSession(path, config)
  ApplyReplicaRebuildWALEntry(path, sessionID, entry)
  ApplyReplicaRebuildBaseBlock(path, sessionID, lba, data)
  MarkReplicaRebuildBaseComplete(path, sessionID, totalBlocks)
  TryCompleteReplicaRebuildSession(path, sessionID)
  CancelReplicaRebuildSession(path, sessionID, reason)
  ReplicaRebuildSession(path) → snapshot

Each method does one thing: validate → WithVolume → delegate to BlockVol.
No wire decoding, no protocol decisions, no state invention. Transport
wiring (sessionControl/walData/sessionData handlers) is the next step.

2 focused tests: skeleton routes correctly, stale session ID rejected.

Updated v2-rebuild-mvp-session-protocol.md with server skeleton section.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 14:53:32 -07:00
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
pingqiuandClaude Opus 4.6 55013e103b feat: Phase 20 Stage 0+1 closure — bootstrap + sustained workload on hardware
Stage 0 (bootstrap closure): PASS on m01/M02
  - create RF=2 sync_all → 10s shipper wait → 4k fsync → publish_healthy
  - Proves: BarrierAccepted observation, ShipperConnected, DurableLSN > 0

Stage 1 (sustained workload): 32/33 actions PASS
  - bootstrap → fio 10s randwrite → dd_write 1M×2 fsync → data checksum
  - Remaining: auto-failover promotion (separate issue)

Key fixes:
  - BarrierAccepted callback: SyncCache success → core DurableLSN update
  - BarrierRejected callback: barrier failures surface to core with reason
  - Shipper state callback for new volumes (not just startup volumes)
  - CatchUpTo ctrl conn reset: prevents stale control channel after recovery
  - CP13-6 max-bytes budget suspended: uses replicaFlushedLSN which can't
    advance without barrier; kills healthy shippers during async writes.
    Will be replaced by v2 negotiated sync/recovery protocol.
  - Barrier diagnostic logging: start/fail/success with reason and LSN
  - Scenario restructured: Stage 0 (bootstrap-closure) + Stage 1 (failover)
  - dd_write: sync_mode param + real stderr capture
  - sw-test-runner suite command: deploy once, run N scenarios
  - WAL size plumbing: proto + API + handler (forward-compatible)

Known: 6 blockvol/server test failures from Barrier() path change
(bounded catch-up in Barrier). Need test updates to match new semantics.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 19:55:12 -07:00
pingqiuandClaude Opus 4.6 44103a1bd7 feat: Phase 20 acceptance fixes + sw-test-runner suite mode
Acceptance rows closed:
- WriteLBA/SyncCache contract: code comments document write-back vs
  durability fence semantics
- RF=2 stable identity: v2bridge always uses SetReplicaAddrs (preserves
  ServerID); blockcmd dispatcher also fixed to use setupPrimaryReplicationMulti;
  test asserts exact expected ReplicaID="vs-2" (not just non-empty)
- Tests treating WriteLBA as commit: replica_read_test rewritten with
  SyncCache as durability fence
- publish_healthy contract: 3 gate tests with hard assertions including
  gate 3 (PrimaryShipperConnected)
- SetReplicaAddr deprecation warning added
- WALShipper.ReplicaID() getter added for identity verification

Test runner enhancements:
- sw-test-runner suite command: build → deploy → run N scenarios in one
  invocation with --skip-deploy support
- Suite YAML definitions for T6 Stage 0 and Stage 1
- deploy action: kill stale processes, clean dirs, cross-compile, upload
- run-phase20-t6.ps1 PowerShell script (deprecated by suite command)

Engine/runtime fixes:
- Recovery executor nil-safety improvements
- Recovery bundle BuildRecoveryBundle defensive checks
- ShipperGroup MinReplicaFlushedLSNAll surface

Docs: acceptance checklist refined, test matrix updated, T6 runbook,
engine maintainer tutorial, design README updated.

26 files changed, ~1600 insertions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 11:30:54 -07:00
pingqiuandClaude Opus 4.6 275c3ee1c7 docs: Phase 20 acceptance checklist — architect-refined signoff matrix
Tighten acceptance matrix with explicit per-boundary rows, signoff
reading split into hard blockers vs product hardening, and clear
rule: architecture-complete ≠ product-complete.

6 hard blockers before T6/T7:
1. WriteLBA/SyncCache/sync_all contract closure
2. Fresh replica bounded catch-up before live tail
3. Timeout/retention-loss classification for catch-up
4. publish_healthy alignment with one protocol contract
5. RF=2 stable identity on all shipping paths
6. Test audit for incorrect WriteLBA==commit assumptions

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 00:12:32 -07:00
pingqiuandClaude Opus 4.6 58aa842802 docs: Phase 20 product acceptance checklist
7-area acceptance matrix mapping current state vs product requirements:
write/durability contract, fresh replica bootstrap, host observation
completeness, serving/publish alignment, snapshot/rebuild convergence,
adapter consistency, test contract alignment.

Each item marked with: current state, required for product, blocks
T6/T7, best test level. Priority ordered into must-close-before-Stage-1,
should-close-before-Stage-2, and can-close-after-T6/T7.

Key diagnosis: architecture-complete, execution-incomplete. The engine
thinks like a product; the data plane still behaves partly like a
prototype. The gap is end-to-end contract closure.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 00:05:22 -07:00
pingqiuandClaude Opus 4.6 d1a16fac03 feat: protocol-aware execution wave — phase gate for live WAL shipping
Add host-side protocol state seam that derives per-replica execution
state from V2 sender/session snapshots and blocks live-tail WAL
shipping while an active recovery session is in progress.

New file: weed/server/block_protocol_state.go
  - replicaProtocolExecutionState derived from engine snapshots
  - LiveEligible=false during active catch-up/rebuild sessions
  - bindProtocolExecutionPolicy wires policy into BlockVol
  - syncProtocolExecutionState called after assignments + core events

Data plane changes:
  - WALShipper.Ship() checks liveShippingPolicy before dial/send
  - BlockVol.SetLiveShippingPolicy persists across shipper group rebuilds
  - ShipperGroup propagates policy to all shippers

Design contract: sw-block/design/v2-protocol-aware-execution.md

Scope: WAL-first rollout only. Prevents illegal live-tail delivery
during active recovery. Does not change snapshot/build behavior or
move backlog. Next wave: bounded WAL catch-up under same contract.

Tests: 4 unit/component tests for phase gate behavior, plus bootstrap
seam tests that confirmed the two pre-existing bugs locally.

13 files changed, 900 insertions, 69 deletions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 23:47:07 -07:00
pingqiuandClaude Opus 4.6 f8e8c2c4d1 docs: fix Phase 20 test count — 48 not 49
Verified by counting: T1(5) + T2(12) + T3(8) + T4(8) + T5(13) + Proto(2) = 48.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 21:06:00 -07:00
pingqiuandClaude Opus 4.6 c7dd90c623 docs: Phase 20 test matrix — update with Tier 1 results + full roster status
Update coverage reading to reflect 49 tests (6 new component tests).
Add full roster status table with per-item strong/bounded/missing
marking and mapped test function names.

Unit+component: 32 of 33 items strong (T4-C7 NVMe bounded).
Integration: 6 of 10 missing (Tier 2 next).
Hardware: 4 of 4 missing (T6/T7 staged plan).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 19:42:13 -07:00
pingqiuandClaude Opus 4.6 6bf9a6c283 test: Phase 20 Tier 1 component tests — wiring proof for CI/CD
6 new component tests closing gaps identified in the test matrix audit:

P20-T4-C3: Missing projection with active V2 core fails closed
  - v2Core != nil, no projection cached → gate with "missing_engine_projection"

P20-T4-C6: Gate actually removes iSCSI target (enforcement)
  - real TargetServer → HasTarget(iqn)==true before gate
  - gate → HasTarget(iqn)==false (DisconnectVolume called)
  - ungate → HasTarget(iqn)==true (AddVolume restores)

P20-T5-C3: FailoverDiagnosticSnapshot carries both mode fields
  - register volume with EngineProjectionMode + ClusterReplicationMode
  - trigger pending rebuild → volume appears in diagnostic
  - diagnostic entry carries both modes from registry lookup

P20-T3-C5: V2PromotionMode diagnostic tri-state
  - disabled / placeholder_fail_closed / transport_ready
  - all three configurations produce correct diagnostic value

P20-T1-C3: EngineProjectionMode proto round-trip
  - set value survives InfoMessageToProto → InfoMessageFromProto
  - empty value produces nil proto field (presence semantics)

P20-T4-C8: ActivationGated proto round-trip
  - gated=true + reason survives round-trip
  - not-gated produces no spurious reason

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 19:39:00 -07:00
pingqiuandClaude Opus 4.6 1c7154a11a docs: Phase 20 test matrix — gap inventory + component test specs
Add detailed coverage mapping of 43 existing tests against the test
roster. Identify 7 missing component tests and 3 missing integration
tests with concrete scenarios, file placement, and must-prove criteria.

Key finding: every tester-found bug during T1-T5 was a wiring bug caught
by reviewing the production path, not by unit tests on pure logic. This
confirms component tests are the highest-value gap for CI/CD protection.

Priority order: Tier 1 (7 component tests, do now), Tier 2 (3 integration
tests, do before hardware), Tier 3 (4 hardware scenarios, T6/T7).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 19:33:55 -07:00
pingqiuandClaude Opus 4.6 3e6155c18e docs: Phase 20 T5 — wire ClusterReplicationMode into diagnostic surface
Add ClusterReplicationMode and EngineProjectionMode to
FailoverVolumeState so each volume in the failover diagnostic
carries its cluster/engine mode at diagnosis time.

FailoverDiagnosticSnapshot() enriches volume entries by looking up
the registry entry for each volume. This covers both the block
volume API (GET /block/volume/{name}) and the failover diagnostic
snapshot surface.

Update phase doc to reflect actual exposure paths.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 18:56:40 -07:00
pingqiuandClaude Opus 4.6 ceb68cc66b fix: Phase 20 T5 — RF2 missing replica degraded + transport signal + API surface
Fix three tester findings on T5:

1. RF2 with missing replicas now reports "degraded" instead of
   "no_replicas". Only RF=1 with no replicas returns "no_replicas".
   Missing replica in an RF2 set is a degraded cluster state.

2. TransportDegraded signal now incorporated: if master-observed
   transport is degraded, ClusterReplicationMode is at least
   "degraded" regardless of individual replica health.

3. API surface exposure: EngineProjectionMode and
   ClusterReplicationMode now appear on blockapi.VolumeInfo and are
   populated in entryToVolumeInfo(). Operators can consume both
   through GET /block/volume/{name} with distinct JSON field names.

12 tests: keepup, catching_up, stale degraded, LSN gap needs_rebuild,
rebuilding role, RF1 no_replicas, RF2 missing degraded, transport
degraded, distinctness, heartbeat update, worst dominates, API
surface distinct naming.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 18:49:37 -07:00
pingqiuandClaude Opus 4.6 013f3e7ccb feat: Phase 20 T5 — ClusterReplicationMode on master
Add ClusterReplicationMode as a distinct master-owned cluster-level
replication health judgment, computed from multi-replica facts:
replica LSN lag, heartbeat freshness, role state. Monotonic: worst
replica state dominates.

Modes: "no_replicas" (RF=1), "keepup" (all healthy), "catching_up"
(replica behind but recoverable), "degraded" (stale heartbeat or
barrier failure), "needs_rebuild" (unrecoverable gap or rebuilding
role).

Distinct from EngineProjectionMode (VS-local engine truth) and
VolumeMode (legacy). They answer different questions, live in
different fields, have different names. Tests explicitly prove the
two can differ without conflict.

Computed in recomputeReplicaState() alongside existing VolumeMode.
Updated on every heartbeat that touches the entry.

9 tests: keepup, catching_up, stale degraded, LSN gap needs_rebuild,
rebuilding role, no_replicas, distinctness from EngineProjectionMode,
heartbeat-driven update, worst-replica-dominates (RF3).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 18:41:44 -07:00
pingqiuandClaude Opus 4.6 9cead1b502 fix: Phase 20 T4 — fail-closed on missing projection + NVMe gate
Fix two tester findings:

1. Missing engine projection now fails closed: if v2Core is active but
   CoreProjection(path) is missing, gate locally with reason
   "missing_engine_projection". Mirrors T2's fail-closed posture.
   Only skips enforcement when V2 core is entirely absent.

2. NVMe/TCP now gated alongside iSCSI: gateServing() calls both
   targetServer.DisconnectVolume() and nvmeServer.RemoveVolume().
   ungateServing() re-registers with both iSCSI and NVMe. A gated
   volume is unreachable through all frontend paths.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 18:29:58 -07:00
pingqiuandClaude Opus 4.6 46f72572c5 fix: Phase 20 T4 — real serving enforcement + wire propagation + runtime ungate
Fix three tester findings on T4 activation gate:

1. Real serving enforcement: evaluateActivationGate now calls
   gateServing() → DisconnectVolume(iqn) on gate (terminates active
   iSCSI sessions, removes volume from target). ungateServing() →
   AddVolume(iqn, adapter) on clear (re-registers volume). This is
   actual serving enforcement, not just bookkeeping.

2. Wire propagation: add activation_gated (field 25) and
   activation_gate_reason (field 26) to proto BlockVolumeInfoMessage.
   Add generated Go fields + getters. Add proto conversion in
   InfoMessageToProto/InfoMessageFromProto. Gate state now rides the
   real VS→master heartbeat wire.

3. Runtime ungate: evaluateActivationGate() now also runs in
   applyCoreEvent() (the observation-driven path), not just
   applyCoreAssignmentEvent(). Recovery/catch-up completion that
   transitions the projection to publish_healthy/replica_ready now
   clears the gate and re-registers the volume automatically.

ClearActivationGate() remains as an explicit override for edge cases
but is no longer the primary ungate path.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 18:22:49 -07:00
pingqiuandClaude Opus 4.6 a27569358b feat: Phase 20 T4 — local activation gate on promoted primary
After assignment executes through V2 core, evaluateActivationGate()
checks the resulting projection locally. If mode is degraded,
needs_rebuild, bootstrap_pending, or allocated_only, the volume is
gated from serving. Gate is enforced immediately after assignment,
before the next heartbeat round-trip.

Gate cleared only when projection reaches publish_healthy or
replica_ready. IsActivationGated() provides the query surface for
iSCSI/NVMe adapter enforcement. Heartbeat carries ActivationGated
and ActivationGateReason fields so master can observe the gated state
(report path, not enforcement path).

activationGated map on BlockService tracks per-volume gate state.
Initialized in constructor. Test helper updated to include it.

6 tests: degraded gates, needs_rebuild gates, healthy clears gate,
gate enforced before heartbeat, recovery re-enables, assignment with
degraded projection triggers gate.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 18:13:20 -07:00
pingqiuandClaude Opus 4.6 f825f08680 fix: Phase 20 T3 — correct V2 promotion observability to tri-state mode
Replace misleading V2PromotionEnabled/V2PromotionReady booleans with
single V2PromotionMode string: "disabled", "placeholder_fail_closed",
or "transport_ready".

Previous V2PromotionReady was true whenever any querier was installed,
including the placeholder that always returns error. Now the diagnostic
accurately distinguishes placeholder (fail-closed until proto regen)
from real gRPC transport.

blockV2EvidenceTransport bool on MasterServer tracks whether the real
transport querier is installed. Currently always false (placeholder).
Set to true only when real gRPC querier replaces the placeholder after
proto regen.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 16:29:12 -07:00
pingqiuandClaude Opus 4.6 2b97cd04b8 fix: Phase 20 T3 — add V2 promotion observability to FailoverDiagnostic
FailoverDiagnostic now carries V2PromotionEnabled and V2PromotionReady
fields. MasterServer.FailoverDiagnosticSnapshot() enriches the failover
state diagnostic with rollout gate visibility so operators can confirm
whether the master is on V1, V2, or V2-fail-closed-placeholder mode.

Update phase-20.md: document default=false rollout policy (safe default
until proto regen enables evidence RPC, then flip to default true).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 16:27:02 -07:00
pingqiuandClaude Opus 4.6 43016e6645 fix: Phase 20 T3 — production wiring + fail-closed on partial evidence
Wire V2 promotion into production binary:
- Add --block.v2Promotion CLI flag on weed master (default false)
- MasterOption.BlockV2Promotion → NewMasterServer wires flag + querier
- defaultBlockVSQueryEvidence placeholder (returns explicit error until
  proto regen on M01 enables gRPC evidence RPC)

Fix three fail-closed violations found by tester:
1. blockV2Promotion=true + nil querier now fails closed with explicit
   log instead of silently falling back to V1
2. Partial evidence (any candidate query failed) now fails closed —
   unreachable candidate may be the most durable, promoting from
   incomplete evidence violates durability-first ordering
3. Clear EngineProjectionMode in applyPromotionLocked (already in
   previous commit, verified in tests here)

2 new tests: NilQuerier_FailsClosed, PartialEvidenceFailure_FailsClosed.
Total T3 tests: 7, all pass. Existing V1 failover tests unaffected.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 16:23:35 -07:00