mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-11-12 06:48:45 +01:00
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>
This commit is contained in:
1 parent
d2d57851b0
commit
49845dd509
3 files changed
+244
No files matched your search
@@ -1109,6 +1109,107 @@ func TestBlockService_ApplyAssignments_RebuildingRole_PreservesLegacyFallbackWit
|
||||
}
|
||||
}
|
||||
|
||||
func TestBlockService_ReplicaRebuildSessionSkeleton_RoutesToVolume(t *testing.T) {
|
||||
bs := newTestBlockServiceDirect(t)
|
||||
path := createTestVolDirect(t, bs, "vol-rebuild-session-skeleton")
|
||||
|
||||
if err := bs.StartReplicaRebuildSession(path, blockvol.RebuildSessionConfig{
|
||||
SessionID: 7,
|
||||
Epoch: 1,
|
||||
BaseLSN: 1,
|
||||
TargetLSN: 1,
|
||||
}); err != nil {
|
||||
t.Fatalf("start rebuild session: %v", err)
|
||||
}
|
||||
|
||||
snap, ok, err := bs.ReplicaRebuildSession(path)
|
||||
if err != nil {
|
||||
t.Fatalf("read rebuild session: %v", err)
|
||||
}
|
||||
if !ok {
|
||||
t.Fatal("expected active rebuild session")
|
||||
}
|
||||
if snap.Config.SessionID != 7 {
|
||||
t.Fatalf("session_id=%d, want 7", snap.Config.SessionID)
|
||||
}
|
||||
if snap.Progress.Phase != blockvol.RebuildPhaseRunning {
|
||||
t.Fatalf("phase=%s, want running", snap.Progress.Phase)
|
||||
}
|
||||
|
||||
if err := bs.ApplyReplicaRebuildWALEntry(path, 7, &blockvol.WALEntry{
|
||||
LSN: 1,
|
||||
Epoch: 1,
|
||||
Type: blockvol.EntryTypeWrite,
|
||||
LBA: 0,
|
||||
Length: 4096,
|
||||
Data: bytes.Repeat([]byte{0xAB}, 4096),
|
||||
}); err != nil {
|
||||
t.Fatalf("apply rebuild WAL entry: %v", err)
|
||||
}
|
||||
|
||||
applied, err := bs.ApplyReplicaRebuildBaseBlock(path, 7, 0, bytes.Repeat([]byte{0xCD}, 4096))
|
||||
if err != nil {
|
||||
t.Fatalf("apply rebuild base block: %v", err)
|
||||
}
|
||||
if applied {
|
||||
t.Fatal("expected base block to be skipped after WAL apply")
|
||||
}
|
||||
|
||||
if err := bs.MarkReplicaRebuildBaseComplete(path, 7, 1); err != nil {
|
||||
t.Fatalf("mark base complete: %v", err)
|
||||
}
|
||||
achieved, completed, err := bs.TryCompleteReplicaRebuildSession(path, 7)
|
||||
if err != nil {
|
||||
t.Fatalf("try complete: %v", err)
|
||||
}
|
||||
if !completed || achieved != 1 {
|
||||
t.Fatalf("achieved=%d completed=%v, want achieved=1 completed=true", achieved, completed)
|
||||
}
|
||||
|
||||
snap, ok, err = bs.ReplicaRebuildSession(path)
|
||||
if err != nil {
|
||||
t.Fatalf("read completed session: %v", err)
|
||||
}
|
||||
if !ok || !snap.Progress.Completed() {
|
||||
t.Fatalf("session completed=%v ok=%v", snap.Progress.Completed(), ok)
|
||||
}
|
||||
|
||||
if err := bs.CancelReplicaRebuildSession(path, 7, "test_done"); err != nil {
|
||||
t.Fatalf("cancel rebuild session: %v", err)
|
||||
}
|
||||
if _, ok, err := bs.ReplicaRebuildSession(path); err != nil {
|
||||
t.Fatalf("read cleared session: %v", err)
|
||||
} else if ok {
|
||||
t.Fatal("expected no active rebuild session after cancel")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBlockService_ReplicaRebuildSessionSkeleton_RejectsStaleSessionID(t *testing.T) {
|
||||
bs := newTestBlockServiceDirect(t)
|
||||
path := createTestVolDirect(t, bs, "vol-rebuild-session-stale")
|
||||
|
||||
if err := bs.StartReplicaRebuildSession(path, blockvol.RebuildSessionConfig{
|
||||
SessionID: 9,
|
||||
Epoch: 1,
|
||||
BaseLSN: 1,
|
||||
TargetLSN: 1,
|
||||
}); err != nil {
|
||||
t.Fatalf("start rebuild session: %v", err)
|
||||
}
|
||||
|
||||
err := bs.ApplyReplicaRebuildWALEntry(path, 8, &blockvol.WALEntry{
|
||||
LSN: 1,
|
||||
Epoch: 1,
|
||||
Type: blockvol.EntryTypeWrite,
|
||||
LBA: 0,
|
||||
Length: 4096,
|
||||
Data: bytes.Repeat([]byte{0xEF}, 4096),
|
||||
})
|
||||
if err == nil {
|
||||
t.Fatal("expected stale session ID to be rejected")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBlockService_BarrierRejected_ExecutesCoreInvalidateSession(t *testing.T) {
|
||||
bs := newTestBlockServiceDirect(t)
|
||||
bs.v2Bridge = newTestControlBridge()
|
||||
|
||||
Reference in new issue
Block a user