Files
seaweedfs/weed/server/blockcmd/service_ops.go
T
pingqiu 43dbebfa04 refactor: close bounded recovery drain and invalidation seams
Move removed-replica drain and replica-scoped invalidation onto explicit core-command paths so the widened multi-replica runtime no longer depends on coarse host-side recovery handling.

Made-with: Cursor
2026-04-04 11:01:12 -07:00

142 lines
4.3 KiB
Go

package blockcmd
import (
engine "github.com/seaweedfs/seaweedfs/sw-block/engine/replication"
"github.com/seaweedfs/seaweedfs/weed/storage/blockvol"
)
// BackendOps owns the concrete backend-facing operations that remain separate
// from server-side command orchestration.
type BackendOps interface {
ApplyRole(assignment blockvol.BlockVolumeAssignment) (bool, error)
StartReceiver(assignment blockvol.BlockVolumeAssignment) (bool, error)
ConfigureShipper(volumeID string, replicas []engine.ReplicaAssignment) (executed bool, shipperConnected bool, err error)
}
// RecoveryCoordinator is the runtime recovery surface used by command ops.
type RecoveryCoordinator interface {
StartRecoveryTask(replicaID string, assignments []blockvol.BlockVolumeAssignment)
DrainRecoveryTask(replicaID, reason string)
ExecutePendingCatchUp(replicaID string, targetLSN uint64) error
ExecutePendingRebuild(replicaID string, targetLSN uint64) error
}
// ProjectionReader provides access to current core publication state.
type ProjectionReader interface {
Projection(volumeID string) (engine.PublicationProjection, bool)
}
// SessionInvalidator invalidates an active sender session.
type SessionInvalidator interface {
InvalidateSession(reason string, targetState engine.ReplicaState)
}
// SenderResolver resolves one sender/session invalidator by replica ID.
type SenderResolver func(replicaID string) SessionInvalidator
// ServiceOps composes backend ops with runtime/server dependencies to satisfy
// the dispatcher Ops interface without requiring a full BlockService pointer.
type ServiceOps struct {
backend BackendOps
recovery RecoveryCoordinator
projection ProjectionReader
senderByID SenderResolver
}
func NewServiceOps(
backend BackendOps,
recovery RecoveryCoordinator,
projection ProjectionReader,
senderByID SenderResolver,
) *ServiceOps {
return &ServiceOps{
backend: backend,
recovery: recovery,
projection: projection,
senderByID: senderByID,
}
}
func (ops *ServiceOps) ApplyRole(assignment blockvol.BlockVolumeAssignment) (bool, error) {
if ops == nil || ops.backend == nil {
return false, nil
}
return ops.backend.ApplyRole(assignment)
}
func (ops *ServiceOps) StartReceiver(assignment blockvol.BlockVolumeAssignment) (bool, error) {
if ops == nil || ops.backend == nil {
return false, nil
}
return ops.backend.StartReceiver(assignment)
}
func (ops *ServiceOps) ConfigureShipper(volumeID string, replicas []engine.ReplicaAssignment) (bool, bool, error) {
if ops == nil || ops.backend == nil {
return false, false, nil
}
return ops.backend.ConfigureShipper(volumeID, replicas)
}
func (ops *ServiceOps) StartRecoveryTask(replicaID string, assignment blockvol.BlockVolumeAssignment) (bool, error) {
if ops == nil || ops.recovery == nil {
return false, nil
}
ops.recovery.StartRecoveryTask(replicaID, []blockvol.BlockVolumeAssignment{assignment})
return true, nil
}
func (ops *ServiceOps) DrainRecoveryTask(replicaID, reason string) (bool, error) {
if ops == nil || ops.recovery == nil {
return false, nil
}
ops.recovery.DrainRecoveryTask(replicaID, reason)
return true, nil
}
func (ops *ServiceOps) InvalidateSession(volumeID, replicaID, reason string) (bool, error) {
if ops == nil || ops.projection == nil || ops.senderByID == nil {
return false, nil
}
if replicaID != "" {
sender := ops.senderByID(replicaID)
if sender == nil {
return false, nil
}
sender.InvalidateSession(reason, engine.StateDisconnected)
return true, nil
}
proj, ok := ops.projection.Projection(volumeID)
if !ok {
return false, nil
}
for _, replicaID := range proj.ReplicaIDs {
sender := ops.senderByID(replicaID)
if sender == nil {
continue
}
sender.InvalidateSession(reason, engine.StateDisconnected)
}
return true, nil
}
func (ops *ServiceOps) StartCatchUp(replicaID string, targetLSN uint64) (bool, error) {
if ops == nil || ops.recovery == nil {
return false, nil
}
if err := ops.recovery.ExecutePendingCatchUp(replicaID, targetLSN); err != nil {
return false, err
}
return true, nil
}
func (ops *ServiceOps) StartRebuild(replicaID string, targetLSN uint64) (bool, error) {
if ops == nil || ops.recovery == nil {
return false, nil
}
if err := ops.recovery.ExecutePendingRebuild(replicaID, targetLSN); err != nil {
return false, err
}
return true, nil
}