Files
seaweedfs/sw-block/engine/replication/outcome.go
T
pingqiuandClaude Opus 4.6 20d70f9fb6 feat: add V2 engine replication core (Phase 05 Slice 1)
Creates sw-block/engine/replication/ — the real V2 engine ownership core,
promoted from sw-block/prototype/enginev2/ with all accepted invariants.

Files:
- types.go: Endpoint, ReplicaState, SessionKind, SessionPhase, FSM transitions
- sender.go: per-replica Sender with full execution + rebuild APIs
- session.go: Session with identity, phases, frozen target, truncation, budget
- registry.go: Registry with reconcile + assignment intent + epoch invalidation
- budget.go: CatchUpBudget (duration, entries, stall detection)
- rebuild.go: RebuildState FSM (snapshot+tail vs full base)
- outcome.go: HandshakeResult + ClassifyRecoveryOutcome

Tests (ownership_test.go, 13 tests):
- Changed-address invalidation (A10)
- Stale session ID rejected at all APIs (A3)
- Stale completion after supersede (A3)
- Epoch bump invalidates all sessions (A3)
- Stale assignment epoch rejected
- Rebuild exclusivity (catch-up APIs rejected)
- Rebuild full lifecycle
- Frozen target rejects chase (A5)
- Budget violation escalates (A5)
- E2E: 3 replicas, 3 outcomes

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 20:51:01 -07:00

31 lines
1.1 KiB
Go

package replication
// HandshakeResult captures what the reconnect handshake reveals about
// a replica's state relative to the primary's lineage-safe boundary.
type HandshakeResult struct {
ReplicaFlushedLSN uint64 // highest LSN durably persisted on replica
CommittedLSN uint64 // lineage-safe recovery target
RetentionStartLSN uint64 // oldest LSN still available in primary WAL
}
// RecoveryOutcome classifies the gap between replica and primary.
type RecoveryOutcome string
const (
OutcomeZeroGap RecoveryOutcome = "zero_gap"
OutcomeCatchUp RecoveryOutcome = "catchup"
OutcomeNeedsRebuild RecoveryOutcome = "needs_rebuild"
)
// ClassifyRecoveryOutcome determines the recovery path from handshake data.
// Zero-gap requires exact equality (FlushedLSN == CommittedLSN).
func ClassifyRecoveryOutcome(result HandshakeResult) RecoveryOutcome {
if result.ReplicaFlushedLSN == result.CommittedLSN {
return OutcomeZeroGap
}
if result.RetentionStartLSN == 0 || result.ReplicaFlushedLSN+1 >= result.RetentionStartLSN {
return OutcomeCatchUp
}
return OutcomeNeedsRebuild
}