mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-10-06 14:31:57 +02:00
refactor: make bounded recovery observation events replica-scoped
Carry replica-scoped addressing through bounded recovery planning and completion events so the core no longer depends on a volume-only observation seam. This preserves the current single-replica catch-up and rebuilding behavior while aligning the observation side with the replica-scoped command path. Made-with: Cursor
This commit is contained in:
1 parent
b304b8e212
commit
16ba70f856
9 files changed
+174
-40
No files matched your search
@@ -7,12 +7,12 @@ import engine "github.com/seaweedfs/seaweedfs/sw-block/engine/replication"
|
||||
// IO bindings and receives completion notifications.
|
||||
type RecoveryCallbacks interface {
|
||||
// OnCatchUpCompleted is called after successful catch-up execution.
|
||||
OnCatchUpCompleted(volumeID string, achievedLSN uint64)
|
||||
OnCatchUpCompleted(volumeID, replicaID string, achievedLSN uint64)
|
||||
|
||||
// OnRebuildCompleted is called after successful rebuild execution.
|
||||
// The host should read the post-rebuild snapshot and emit the
|
||||
// appropriate core event.
|
||||
OnRebuildCompleted(volumeID string, plan *engine.RecoveryPlan)
|
||||
OnRebuildCompleted(volumeID, replicaID string, plan *engine.RecoveryPlan)
|
||||
}
|
||||
|
||||
// ExecuteCatchUpPlan runs a catch-up plan using the supplied IO binding
|
||||
@@ -22,6 +22,7 @@ func ExecuteCatchUpPlan(
|
||||
plan *engine.RecoveryPlan,
|
||||
io engine.CatchUpIO,
|
||||
volumeID string,
|
||||
replicaID string,
|
||||
callbacks RecoveryCallbacks,
|
||||
) error {
|
||||
exec := engine.NewCatchUpExecutor(driver, plan)
|
||||
@@ -34,7 +35,7 @@ func ExecuteCatchUpPlan(
|
||||
if achievedLSN == 0 {
|
||||
achievedLSN = plan.CatchUpStartLSN
|
||||
}
|
||||
callbacks.OnCatchUpCompleted(volumeID, achievedLSN)
|
||||
callbacks.OnCatchUpCompleted(volumeID, replicaID, achievedLSN)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -46,6 +47,7 @@ func ExecuteRebuildPlan(
|
||||
plan *engine.RecoveryPlan,
|
||||
io engine.RebuildIO,
|
||||
volumeID string,
|
||||
replicaID string,
|
||||
callbacks RecoveryCallbacks,
|
||||
) error {
|
||||
exec := engine.NewRebuildExecutor(driver, plan)
|
||||
@@ -54,7 +56,7 @@ func ExecuteRebuildPlan(
|
||||
return err
|
||||
}
|
||||
if callbacks != nil {
|
||||
callbacks.OnRebuildCompleted(volumeID, plan)
|
||||
callbacks.OnRebuildCompleted(volumeID, replicaID, plan)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -7,24 +7,28 @@ import (
|
||||
)
|
||||
|
||||
type fakeCallbacks struct {
|
||||
catchUpCalled bool
|
||||
catchUpVol string
|
||||
catchUpLSN uint64
|
||||
catchUpCalled bool
|
||||
catchUpVol string
|
||||
catchUpReplica string
|
||||
catchUpLSN uint64
|
||||
|
||||
rebuildCalled bool
|
||||
rebuildVol string
|
||||
rebuildPlan *engine.RecoveryPlan
|
||||
rebuildCalled bool
|
||||
rebuildVol string
|
||||
rebuildReplica string
|
||||
rebuildPlan *engine.RecoveryPlan
|
||||
}
|
||||
|
||||
func (f *fakeCallbacks) OnCatchUpCompleted(volumeID string, achievedLSN uint64) {
|
||||
func (f *fakeCallbacks) OnCatchUpCompleted(volumeID, replicaID string, achievedLSN uint64) {
|
||||
f.catchUpCalled = true
|
||||
f.catchUpVol = volumeID
|
||||
f.catchUpReplica = replicaID
|
||||
f.catchUpLSN = achievedLSN
|
||||
}
|
||||
|
||||
func (f *fakeCallbacks) OnRebuildCompleted(volumeID string, plan *engine.RecoveryPlan) {
|
||||
func (f *fakeCallbacks) OnRebuildCompleted(volumeID, replicaID string, plan *engine.RecoveryPlan) {
|
||||
f.rebuildCalled = true
|
||||
f.rebuildVol = volumeID
|
||||
f.rebuildReplica = replicaID
|
||||
f.rebuildPlan = plan
|
||||
}
|
||||
|
||||
@@ -50,7 +54,7 @@ func TestExecuteCatchUpPlan_CallsbackOnSuccess(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = ExecuteCatchUpPlan(driver, plan, &noopCatchUpIO{}, "vol1", cb)
|
||||
err = ExecuteCatchUpPlan(driver, plan, &noopCatchUpIO{}, "vol1", "vol1/vs2", cb)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -60,6 +64,9 @@ func TestExecuteCatchUpPlan_CallsbackOnSuccess(t *testing.T) {
|
||||
if cb.catchUpVol != "vol1" {
|
||||
t.Fatalf("vol=%s", cb.catchUpVol)
|
||||
}
|
||||
if cb.catchUpReplica != "vol1/vs2" {
|
||||
t.Fatalf("replica=%s", cb.catchUpReplica)
|
||||
}
|
||||
if cb.catchUpLSN != 100 {
|
||||
t.Fatalf("achievedLSN=%d", cb.catchUpLSN)
|
||||
}
|
||||
@@ -74,7 +81,7 @@ func TestExecuteCatchUpPlan_AchievedLSNMatchesTarget(t *testing.T) {
|
||||
}
|
||||
// The plan's CatchUpTarget is derived from storage state.
|
||||
// The callback should receive that same target as achievedLSN.
|
||||
err = ExecuteCatchUpPlan(driver, plan, &noopCatchUpIO{}, "vol1", cb)
|
||||
err = ExecuteCatchUpPlan(driver, plan, &noopCatchUpIO{}, "vol1", "vol1/vs2", cb)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -100,7 +107,7 @@ func TestExecuteRebuildPlan_CallsbackOnSuccess(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = ExecuteRebuildPlan(driver, plan, &noopRebuildIO{}, "vol2", cb)
|
||||
err = ExecuteRebuildPlan(driver, plan, &noopRebuildIO{}, "vol2", "vol2/vs2", cb)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -110,6 +117,9 @@ func TestExecuteRebuildPlan_CallsbackOnSuccess(t *testing.T) {
|
||||
if cb.rebuildVol != "vol2" {
|
||||
t.Fatalf("vol=%s", cb.rebuildVol)
|
||||
}
|
||||
if cb.rebuildReplica != "vol2/vs2" {
|
||||
t.Fatalf("replica=%s", cb.rebuildReplica)
|
||||
}
|
||||
if cb.rebuildPlan == nil {
|
||||
t.Fatal("rebuild plan not passed to callback")
|
||||
}
|
||||
@@ -122,7 +132,7 @@ func TestExecuteCatchUpPlan_NilCallbacksSafe(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// nil callbacks should not panic.
|
||||
if err := ExecuteCatchUpPlan(driver, plan, &noopCatchUpIO{}, "vol1", nil); err != nil {
|
||||
if err := ExecuteCatchUpPlan(driver, plan, &noopCatchUpIO{}, "vol1", "vol1/vs2", nil); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
@@ -132,12 +142,12 @@ func TestExecuteCatchUpPlan_NilCallbacksSafe(t *testing.T) {
|
||||
type noopCatchUpIO struct{}
|
||||
|
||||
func (noopCatchUpIO) StreamWALEntries(start, end uint64) (uint64, error) { return end, nil }
|
||||
func (noopCatchUpIO) TruncateWAL(lsn uint64) error { return nil }
|
||||
func (noopCatchUpIO) TruncateWAL(lsn uint64) error { return nil }
|
||||
|
||||
type noopRebuildIO struct{}
|
||||
|
||||
func (noopRebuildIO) StreamWALEntries(start, end uint64) (uint64, error) { return end, nil }
|
||||
func (noopRebuildIO) TruncateWAL(lsn uint64) error { return nil }
|
||||
func (noopRebuildIO) TruncateWAL(lsn uint64) error { return nil }
|
||||
func (noopRebuildIO) TransferSnapshot(lsn uint64) error { return nil }
|
||||
func (noopRebuildIO) TransferFullBase(lsn uint64) (uint64, error) { return lsn, nil }
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ type RebuildCompletionStatus struct {
|
||||
// DeriveRebuildCommitted computes the RebuildCommitted event from
|
||||
// post-rebuild status and the original plan. This is the reusable
|
||||
// shaping logic — the host only needs to supply the raw snapshot values.
|
||||
func DeriveRebuildCommitted(volumeID string, status RebuildCompletionStatus, plan *engine.RecoveryPlan) engine.RebuildCommitted {
|
||||
func DeriveRebuildCommitted(volumeID, replicaID string, status RebuildCompletionStatus, plan *engine.RecoveryPlan) engine.RebuildCommitted {
|
||||
flushedLSN := status.CommittedLSN
|
||||
if flushedLSN == 0 {
|
||||
flushedLSN = plan.RebuildTargetLSN
|
||||
@@ -27,6 +27,7 @@ func DeriveRebuildCommitted(volumeID string, status RebuildCompletionStatus, pla
|
||||
achievedLSN = checkpointLSN
|
||||
}
|
||||
return engine.RebuildCommitted{
|
||||
ReplicaID: replicaID,
|
||||
ID: volumeID,
|
||||
AchievedLSN: achievedLSN,
|
||||
FlushedLSN: flushedLSN,
|
||||
|
||||
Reference in new issue
Block a user