Browse Source
fix: deliver assignment intent to real engine orchestrator, not discard
fix: deliver assignment intent to real engine orchestrator, not discard
Finding 1: ProcessAssignments now calls v2Orchestrator.ProcessAssignment - BlockService.v2Orchestrator field (RecoveryOrchestrator) - ProcessAssignment result logged at glog V(1) - No more `_ = intent` — engine state actually changes Finding 2: localServerID documented as interim - BlockService.localServerID = listenAddr (transport-shaped) - Field doc explicitly states: INTERIM, should be registry-assigned - Used only for replica/rebuild local identity 3 integration tests (qa_block_v2bridge_test.go): - CreatesEngineSender: ProcessAssignment → engine has sender + session - EpochBump: epoch 1 → invalidate → epoch 2 → new session - AddressChange: same ServerID, different IP → sender preserved, endpoint updated Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>feature/sw-block
2 changed files with 183 additions and 7 deletions
@ -0,0 +1,158 @@ |
|||
package weed_server |
|||
|
|||
import ( |
|||
"testing" |
|||
|
|||
engine "github.com/seaweedfs/seaweedfs/sw-block/engine/replication" |
|||
"github.com/seaweedfs/seaweedfs/weed/storage/blockvol" |
|||
"github.com/seaweedfs/seaweedfs/weed/storage/blockvol/v2bridge" |
|||
) |
|||
|
|||
func newTestControlBridge() *v2bridge.ControlBridge { |
|||
return v2bridge.NewControlBridge() |
|||
} |
|||
|
|||
func newTestOrchestrator() *engine.RecoveryOrchestrator { |
|||
return engine.NewRecoveryOrchestrator() |
|||
} |
|||
|
|||
// ============================================================
|
|||
// Phase 08 P1: ProcessAssignments → V2 engine state tests
|
|||
// Proves real assignment delivery changes engine state.
|
|||
// ============================================================
|
|||
|
|||
func TestV2Bridge_ProcessAssignment_CreatesEngineSender(t *testing.T) { |
|||
bs := &BlockService{ |
|||
v2Bridge: newTestControlBridge(), |
|||
v2Orchestrator: newTestOrchestrator(), |
|||
localServerID: "vs1:9333", |
|||
} |
|||
|
|||
// Real assignment from master.
|
|||
bs.ProcessAssignments([]blockvol.BlockVolumeAssignment{ |
|||
{ |
|||
Path: "pvc-test-1", |
|||
Epoch: 1, |
|||
Role: uint32(blockvol.RolePrimary), |
|||
ReplicaServerID: "vs2", |
|||
ReplicaDataAddr: "10.0.0.2:9333", |
|||
ReplicaCtrlAddr: "10.0.0.2:9334", |
|||
}, |
|||
}) |
|||
|
|||
// Engine should have a sender for this replica.
|
|||
orch := bs.V2Orchestrator() |
|||
sender := orch.Registry.Sender("pvc-test-1/vs2") |
|||
if sender == nil { |
|||
t.Fatal("engine should have sender for pvc-test-1/vs2 after ProcessAssignment") |
|||
} |
|||
if !sender.HasActiveSession() { |
|||
t.Fatal("sender should have active session (catch-up recovery)") |
|||
} |
|||
} |
|||
|
|||
func TestV2Bridge_ProcessAssignment_EpochBump(t *testing.T) { |
|||
bs := &BlockService{ |
|||
v2Bridge: newTestControlBridge(), |
|||
v2Orchestrator: newTestOrchestrator(), |
|||
localServerID: "vs1:9333", |
|||
} |
|||
|
|||
// Epoch 1 assignment.
|
|||
bs.ProcessAssignments([]blockvol.BlockVolumeAssignment{ |
|||
{ |
|||
Path: "pvc-test-1", |
|||
Epoch: 1, |
|||
Role: uint32(blockvol.RolePrimary), |
|||
ReplicaServerID: "vs2", |
|||
ReplicaDataAddr: "10.0.0.2:9333", |
|||
ReplicaCtrlAddr: "10.0.0.2:9334", |
|||
}, |
|||
}) |
|||
|
|||
orch := bs.V2Orchestrator() |
|||
|
|||
// Epoch bump.
|
|||
orch.InvalidateEpoch(2) |
|||
orch.UpdateSenderEpoch("pvc-test-1/vs2", 2) |
|||
|
|||
// Epoch 2 assignment.
|
|||
bs.ProcessAssignments([]blockvol.BlockVolumeAssignment{ |
|||
{ |
|||
Path: "pvc-test-1", |
|||
Epoch: 2, |
|||
Role: uint32(blockvol.RolePrimary), |
|||
ReplicaServerID: "vs2", |
|||
ReplicaDataAddr: "10.0.0.2:9333", |
|||
ReplicaCtrlAddr: "10.0.0.2:9334", |
|||
}, |
|||
}) |
|||
|
|||
sender := orch.Registry.Sender("pvc-test-1/vs2") |
|||
if sender == nil { |
|||
t.Fatal("sender should exist at epoch 2") |
|||
} |
|||
if !sender.HasActiveSession() { |
|||
t.Fatal("should have new session at epoch 2") |
|||
} |
|||
|
|||
// Log shows invalidation.
|
|||
hasInvalidation := false |
|||
for _, e := range orch.Log.EventsFor("pvc-test-1/vs2") { |
|||
if e.Event == "session_invalidated" { |
|||
hasInvalidation = true |
|||
} |
|||
} |
|||
if !hasInvalidation { |
|||
t.Fatal("log must show session invalidation on epoch bump") |
|||
} |
|||
} |
|||
|
|||
func TestV2Bridge_ProcessAssignment_AddressChange(t *testing.T) { |
|||
bs := &BlockService{ |
|||
v2Bridge: newTestControlBridge(), |
|||
v2Orchestrator: newTestOrchestrator(), |
|||
localServerID: "vs1:9333", |
|||
} |
|||
|
|||
// First assignment.
|
|||
bs.ProcessAssignments([]blockvol.BlockVolumeAssignment{ |
|||
{ |
|||
Path: "pvc-test-1", |
|||
Epoch: 1, |
|||
Role: uint32(blockvol.RolePrimary), |
|||
ReplicaServerID: "vs2", |
|||
ReplicaDataAddr: "10.0.0.2:9333", |
|||
ReplicaCtrlAddr: "10.0.0.2:9334", |
|||
}, |
|||
}) |
|||
|
|||
orch := bs.V2Orchestrator() |
|||
sender := orch.Registry.Sender("pvc-test-1/vs2") |
|||
if sender == nil { |
|||
t.Fatal("sender should exist") |
|||
} |
|||
|
|||
// Same ServerID, different address (address change).
|
|||
bs.ProcessAssignments([]blockvol.BlockVolumeAssignment{ |
|||
{ |
|||
Path: "pvc-test-1", |
|||
Epoch: 1, |
|||
Role: uint32(blockvol.RolePrimary), |
|||
ReplicaServerID: "vs2", |
|||
ReplicaDataAddr: "10.0.0.5:9333", // different IP
|
|||
ReplicaCtrlAddr: "10.0.0.5:9334", |
|||
}, |
|||
}) |
|||
|
|||
// Sender identity preserved (same pointer).
|
|||
senderAfter := orch.Registry.Sender("pvc-test-1/vs2") |
|||
if senderAfter != sender { |
|||
t.Fatal("sender identity must be preserved across address change") |
|||
} |
|||
|
|||
// Endpoint updated.
|
|||
if senderAfter.Endpoint().DataAddr != "10.0.0.5:9333" { |
|||
t.Fatalf("endpoint not updated: %s", senderAfter.Endpoint().DataAddr) |
|||
} |
|||
} |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue