Files
seaweedfs/sw-block/runtime/volumev2/failover_adapter.go
T
pingqiuandClaude Opus 4.6 b82df09856 feat: Phase 18 M1 — transport-backed RF2 failover runtime
M1 milestone: failover evidence crosses transport/session seam.

Adapter seam (failover_adapter.go):
- FailoverEvidenceAdapter: query-side (promotion evidence + replica summary)
- FailoverTakeoverAdapter: execution-side (prepare + gate)
- FailoverTarget: binds NodeID + both adapters
- NewInProcessFailoverTarget: factory for in-process case

Transport seam (failover_evidence_transport.go):
- FailoverEvidenceTransport: request/response interface with nodeID routing
- FailoverEvidenceHandler: server-side registration
- InMemoryFailoverEvidenceTransport: first transport impl (in-memory)
- NewHybridInProcessFailoverTarget: transport-backed evidence + local takeover

Runtime manager (runtime_manager.go):
- InProcessRuntimeManager: participant registry + ExecuteFailover entry point
- Persisted failover snapshots/results per-volume and global-last

All failover paths (session/driver/manager) now go through adapter seam.
Old FailoverParticipant preserved as compatibility wrapper only.

Phase 18 docs: phase-18.md (M1-M5 structure), log, decisions.
Design docs updated: kernel-closure-review, claim-and-evidence ledger.

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

85 lines
3.1 KiB
Go

package volumev2
import (
"fmt"
"github.com/seaweedfs/seaweedfs/sw-block/runtime/masterv2"
"github.com/seaweedfs/seaweedfs/sw-block/runtime/protocolv2"
)
// FailoverEvidenceAdapter is the transport/session-facing query surface used by
// failover orchestration. Future remote implementations should satisfy this
// contract without changing failover session logic.
type FailoverEvidenceAdapter interface {
QueryPromotionEvidence(masterv2.PromotionQueryRequest) (masterv2.PromotionQueryResponse, error)
QueryReplicaSummary(protocolv2.ReplicaSummaryRequest) (protocolv2.ReplicaSummaryResponse, error)
}
// FailoverTakeoverAdapter is the selected-primary execution surface used after
// masterv2 authorizes promotion. Future implementations may be local or remote,
// but takeover ownership remains on the selected primary side.
type FailoverTakeoverAdapter interface {
PreparePrimaryTakeover(PrimaryTakeoverPlan) (ReconstructedPrimaryTruth, error)
GatePrimaryActivation(volumeName string, truth ReconstructedPrimaryTruth) error
}
// FailoverTarget binds one stable node id to the evidence and takeover adapters
// used by failover orchestration.
type FailoverTarget struct {
NodeID string
Evidence FailoverEvidenceAdapter
Takeover FailoverTakeoverAdapter
}
// NewInProcessFailoverTarget builds the first adapter-backed target from one
// in-process volumev2 node.
func NewInProcessFailoverTarget(node *Node) (FailoverTarget, error) {
if node == nil {
return FailoverTarget{}, fmt.Errorf("volumev2: node is nil")
}
if node.NodeID() == "" {
return FailoverTarget{}, fmt.Errorf("volumev2: node id is required")
}
return FailoverTarget{
NodeID: node.NodeID(),
Evidence: inProcessFailoverEvidenceAdapter{node: node},
Takeover: inProcessFailoverTakeoverAdapter{node: node},
}, nil
}
type inProcessFailoverEvidenceAdapter struct {
node *Node
}
func (a inProcessFailoverEvidenceAdapter) QueryPromotionEvidence(req masterv2.PromotionQueryRequest) (masterv2.PromotionQueryResponse, error) {
if a.node == nil {
return masterv2.PromotionQueryResponse{}, fmt.Errorf("volumev2: in-process evidence node is nil")
}
return a.node.QueryPromotionEvidence(req)
}
func (a inProcessFailoverEvidenceAdapter) QueryReplicaSummary(req protocolv2.ReplicaSummaryRequest) (protocolv2.ReplicaSummaryResponse, error) {
if a.node == nil {
return protocolv2.ReplicaSummaryResponse{}, fmt.Errorf("volumev2: in-process evidence node is nil")
}
return a.node.QueryReplicaSummary(req)
}
type inProcessFailoverTakeoverAdapter struct {
node *Node
}
func (a inProcessFailoverTakeoverAdapter) PreparePrimaryTakeover(plan PrimaryTakeoverPlan) (ReconstructedPrimaryTruth, error) {
if a.node == nil {
return ReconstructedPrimaryTruth{}, fmt.Errorf("volumev2: in-process takeover node is nil")
}
return a.node.PreparePrimaryTakeover(plan)
}
func (a inProcessFailoverTakeoverAdapter) GatePrimaryActivation(volumeName string, truth ReconstructedPrimaryTruth) error {
if a.node == nil {
return fmt.Errorf("volumev2: in-process takeover node is nil")
}
return a.node.GatePrimaryActivation(volumeName, truth)
}