Files
seaweedfs/weed/server/block_rebuild_session.go
T
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

127 lines
4.6 KiB
Go

package weed_server
import (
"fmt"
"github.com/seaweedfs/seaweedfs/weed/storage/blockvol"
)
// ReplicaRebuildSessionSnapshot is the host-visible rebuild session view for one
// local replica volume.
//
// This is the server-layer skeleton for future transport wiring:
// - sessionControl(start_rebuild) -> StartReplicaRebuildSession
// - walData -> ApplyReplicaRebuildWALEntry
// - sessionData -> ApplyReplicaRebuildBaseBlock
// - sessionData EOF -> MarkReplicaRebuildBaseComplete
// - sessionAck/progress poll -> ReplicaRebuildSession
// - completion gate -> TryCompleteReplicaRebuildSession
// - cancel/supersede -> CancelReplicaRebuildSession
//
// These hooks intentionally do not decode wire messages or decide protocol
// semantics. They only route host-side intent into the local BlockVol surface.
type ReplicaRebuildSessionSnapshot struct {
Path string
Config blockvol.RebuildSessionConfig
Progress blockvol.RebuildSessionProgress
}
// StartReplicaRebuildSession installs one local rebuild session for the
// specified replica volume.
func (bs *BlockService) StartReplicaRebuildSession(path string, config blockvol.RebuildSessionConfig) error {
if bs == nil || bs.blockStore == nil {
return fmt.Errorf("block service not enabled")
}
return bs.blockStore.WithVolume(path, func(vol *blockvol.BlockVol) error {
return vol.StartRebuildSession(config)
})
}
// ApplyReplicaRebuildWALEntry routes one WAL data message into the active local
// rebuild session after session ID validation.
func (bs *BlockService) ApplyReplicaRebuildWALEntry(path string, sessionID uint64, entry *blockvol.WALEntry) error {
if bs == nil || bs.blockStore == nil {
return fmt.Errorf("block service not enabled")
}
return bs.blockStore.WithVolume(path, func(vol *blockvol.BlockVol) error {
return vol.ApplyRebuildSessionWALEntry(sessionID, entry)
})
}
// ApplyReplicaRebuildBaseBlock routes one base-lane block into the active local
// rebuild session after session ID validation.
func (bs *BlockService) ApplyReplicaRebuildBaseBlock(path string, sessionID uint64, lba uint64, data []byte) (bool, error) {
if bs == nil || bs.blockStore == nil {
return false, fmt.Errorf("block service not enabled")
}
applied := false
err := bs.blockStore.WithVolume(path, func(vol *blockvol.BlockVol) error {
var err error
applied, err = vol.ApplyRebuildSessionBaseBlock(sessionID, lba, data)
return err
})
return applied, err
}
// MarkReplicaRebuildBaseComplete closes the base-copy lane for one local
// rebuild session.
func (bs *BlockService) MarkReplicaRebuildBaseComplete(path string, sessionID uint64, totalBlocks uint64) error {
if bs == nil || bs.blockStore == nil {
return fmt.Errorf("block service not enabled")
}
return bs.blockStore.WithVolume(path, func(vol *blockvol.BlockVol) error {
return vol.MarkRebuildSessionBaseComplete(sessionID, totalBlocks)
})
}
// TryCompleteReplicaRebuildSession evaluates the local dual-lane completion
// gate for one rebuild session.
func (bs *BlockService) TryCompleteReplicaRebuildSession(path string, sessionID uint64) (uint64, bool, error) {
if bs == nil || bs.blockStore == nil {
return 0, false, fmt.Errorf("block service not enabled")
}
var achieved uint64
var completed bool
err := bs.blockStore.WithVolume(path, func(vol *blockvol.BlockVol) error {
var err error
achieved, completed, err = vol.TryCompleteRebuildSession(sessionID)
return err
})
return achieved, completed, err
}
// CancelReplicaRebuildSession cancels the local rebuild session. Used by
// explicit cancel, supersede, or transport teardown paths.
func (bs *BlockService) CancelReplicaRebuildSession(path string, sessionID uint64, reason string) error {
if bs == nil || bs.blockStore == nil {
return fmt.Errorf("block service not enabled")
}
return bs.blockStore.WithVolume(path, func(vol *blockvol.BlockVol) error {
return vol.CancelRebuildSession(sessionID, reason)
})
}
// ReplicaRebuildSession returns the current local rebuild session snapshot for
// sessionAck construction or diagnostics.
func (bs *BlockService) ReplicaRebuildSession(path string) (ReplicaRebuildSessionSnapshot, bool, error) {
if bs == nil || bs.blockStore == nil {
return ReplicaRebuildSessionSnapshot{}, false, fmt.Errorf("block service not enabled")
}
var snap ReplicaRebuildSessionSnapshot
var ok bool
err := bs.blockStore.WithVolume(path, func(vol *blockvol.BlockVol) error {
cfg, progress, found := vol.ActiveRebuildSession()
if !found {
return nil
}
ok = true
snap = ReplicaRebuildSessionSnapshot{
Path: path,
Config: cfg,
Progress: progress,
}
return nil
})
return snap, ok, err
}