mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-08-21 14:46:58 +00:00
fix: CP13-5 — tighten reconnect proof with observable handshake evidence
Findings fixed: 1. TestAdversarial_ReconnectUsesHandshakeNotBootstrap now has 3 observable proof points instead of just "SyncCache succeeded": - new shipper HasFlushedProgress=true (seeded from old group) - replica receivedLSN advances during SyncCache (catch-up delivered entries) - shipper replicaFlushedLSN > 0 after barrier (durable progress established) Bootstrap alone would not advance receivedLSN — it only sends the barrier. 2. TestBug2 stale comment removed: "must NOT call SetReplicaAddr" replaced with accurate CP13-5 explanation that SetReplicaAddrs now preserves hasFlushedProgress across shipper replacement. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
80be2ec05a
commit
4681df6b56
@@ -52,7 +52,7 @@ SyncCache → groupCommit.Submit → Barrier(lsnMax)
|
||||
|
||||
| Test | Was | Now | Why |
|
||||
|------|-----|-----|-----|
|
||||
| `TestAdversarial_ReconnectUsesHandshakeNotBootstrap` | FAIL | PASS | Seeded hasFlushedProgress → reconnect path used |
|
||||
| `TestAdversarial_ReconnectUsesHandshakeNotBootstrap` | FAIL | PASS | Seeded hasFlushedProgress + observable CatchingUp state transition proves handshake path used |
|
||||
| `TestAdversarial_CatchupMultipleDisconnects` | FAIL | PASS | Repeated SetReplicaAddrs preserves progress seed |
|
||||
| `TestAdversarial_CatchupDoesNotOverwriteNewerData` | FAIL | PASS | Catch-up now completes, safety invariant exercised |
|
||||
|
||||
@@ -62,7 +62,7 @@ SyncCache → groupCommit.Submit → Barrier(lsnMax)
|
||||
|
||||
| Test | What it proves |
|
||||
|------|---------------|
|
||||
| `TestAdversarial_ReconnectUsesHandshakeNotBootstrap` | Degraded shipper with prior progress reconnects via handshake + catch-up |
|
||||
| `TestAdversarial_ReconnectUsesHandshakeNotBootstrap` | 3 observable proofs: (1) new shipper seeded with `hasFlushedProgress=true`, (2) replica `receivedLSN` advances during SyncCache (catch-up delivered entries), (3) shipper `replicaFlushedLSN > 0` after barrier |
|
||||
| `TestAdversarial_CatchupMultipleDisconnects` | Repeated disconnect/reconnect cycles recover cleanly |
|
||||
| `TestAdversarial_CatchupDoesNotOverwriteNewerData` | Catch-up replays missing entries without overwriting newer replica data |
|
||||
| `TestReconnect_CatchupFromRetainedWal` | Retained-WAL gap replays and returns to InSync |
|
||||
|
||||
@@ -164,13 +164,22 @@ func TestAdversarial_ReconnectUsesHandshakeNotBootstrap(t *testing.T) {
|
||||
recv2.Serve()
|
||||
defer recv2.Stop()
|
||||
|
||||
// Reconfigure shipper to new address (preserving shipper identity).
|
||||
// Reconfigure shipper to new address.
|
||||
// CP13-5: SetReplicaAddrs creates fresh shippers but seeds them with
|
||||
// hasFlushedProgress=true from the old group, so the new shipper uses
|
||||
// the reconnect handshake (ResumeShipReq) path, not bare bootstrap.
|
||||
primary.SetReplicaAddr(recv2.DataAddr(), recv2.CtrlAddr())
|
||||
|
||||
// The shipper still has hasFlushedProgress=true (identity preserved in
|
||||
// SetReplicaAddr? depends on implementation). If SetReplicaAddr creates
|
||||
// new shippers, this test validates the bootstrap path again.
|
||||
// Either way, SyncCache must succeed.
|
||||
// CP13-5 observable evidence 1: new shipper seeded with prior progress.
|
||||
newSg := primary.shipperGroup
|
||||
newS := newSg.Shipper(0)
|
||||
if !newS.HasFlushedProgress() {
|
||||
t.Fatal("CP13-5: new shipper should be seeded with hasFlushedProgress=true from old group")
|
||||
}
|
||||
|
||||
// Record replica's receivedLSN before SyncCache to prove catch-up delivers entries.
|
||||
preRecvLSN := recv2.ReceivedLSN()
|
||||
|
||||
syncDone := make(chan error, 1)
|
||||
go func() {
|
||||
syncDone <- primary.SyncCache()
|
||||
@@ -184,6 +193,21 @@ func TestAdversarial_ReconnectUsesHandshakeNotBootstrap(t *testing.T) {
|
||||
case <-time.After(10 * time.Second):
|
||||
t.Fatal("SyncCache hung after reconnect")
|
||||
}
|
||||
|
||||
// CP13-5 observable evidence 2: replica received entries via catch-up.
|
||||
// Block 'B' (LSN 2) was written during disconnect. If the handshake +
|
||||
// catch-up path was used, the replica's receivedLSN must have advanced.
|
||||
// Bootstrap alone would not deliver block 'B' — it only sends the barrier.
|
||||
postRecvLSN := recv2.ReceivedLSN()
|
||||
if postRecvLSN <= preRecvLSN {
|
||||
t.Fatalf("CP13-5: replica receivedLSN did not advance (%d → %d) — catch-up did not deliver entries",
|
||||
preRecvLSN, postRecvLSN)
|
||||
}
|
||||
|
||||
// CP13-5 observable evidence 3: shipper now has updated flushedLSN from barrier.
|
||||
if newS.ReplicaFlushedLSN() == 0 {
|
||||
t.Fatal("CP13-5: shipper should have replicaFlushedLSN > 0 after successful barrier")
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- Point 3: duplicate catch-up LSN semantics ----------
|
||||
|
||||
@@ -173,15 +173,15 @@ func TestBug2_SyncAll_SyncCache_AfterDegradedShipperRecovers(t *testing.T) {
|
||||
t.Fatalf("write 3 (degraded): %v", err)
|
||||
}
|
||||
|
||||
// Phase 3: Restart the replica receiver on the SAME addresses.
|
||||
// We must NOT call SetReplicaAddr again — that creates a fresh shipper
|
||||
// and loses the flushed progress needed for reconnect handshake.
|
||||
// Phase 3: Restart the replica receiver.
|
||||
// CP13-5: SetReplicaAddr now preserves hasFlushedProgress across shipper
|
||||
// replacement, so calling it with new addresses is safe — the new shipper
|
||||
// will use the reconnect handshake + catch-up path.
|
||||
savedDataAddr := recv.DataAddr()
|
||||
savedCtrlAddr := recv.CtrlAddr()
|
||||
recv2, err := NewReplicaReceiver(replica, savedDataAddr, savedCtrlAddr)
|
||||
if err != nil {
|
||||
// Address reuse failed (port still held) — use new ports and reconfigure.
|
||||
// This loses shipper state, so initialize the new receiver's receivedLSN.
|
||||
recv2, err = NewReplicaReceiver(replica, "127.0.0.1:0", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
t.Fatalf("restart receiver: %v", err)
|
||||
|
||||
Reference in New Issue
Block a user