fix: CP13-2 — use advertisedIP (routable), not localServerID (opaque)

Bug: setupReplicaReceiver derived the advertised host from localServerID,
which can be an opaque string (from -id flag, e.g., "my-custom-server-id").
This would publish unusable endpoints like "my-custom-server-id:14260".

Fix:
- volume_server_block.go: add advertisedIP field (always a real IP from
  -ip flag), use it instead of localServerID for replica canonicalization
- volume.go: wire *v.ip → blockService.SetAdvertisedIP() at startup
- blockvol.go: StartReplicaReceiver variadic advertisedHost unchanged

Proof (sync_all_bug_test.go TestBug3, 4 sub-cases):
- fallback: wildcard bind without advertisedHost → outbound-IP
- advertisedHost: explicit IP appears in exported addresses
- StartReplicaReceiver_API: public API forwards host correctly
- opaque_identity_not_routable: proves opaque string produces
  non-routable address, confirming production must use advertisedIP

Identity vs transport separation preserved:
- localServerID: stable identity for V2 control (may be opaque)
- advertisedIP: routable IP for transport endpoints (always real IP)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
pingqiuandClaude Opus 4.6 committed 2026-04-02 20:51:47 -07:00
1 parent 2d47383df7
commit 4bdf6c604e
4 files changed
+51 -12

No files matched your search

@@ -104,6 +104,29 @@ func TestBug3_ReplicaAddr_MustBeIPPort_WildcardBind(t *testing.T) {
t.Fatalf("CtrlAddr %q does not use advertisedHost 10.0.0.99", info.CtrlAddr)
}
})
// Case 4: opaque non-IP advertisedHost must NOT produce a usable routable address.
// CP13-2: this proves that passing an opaque server identity (e.g., from -id flag)
// as the advertised host would produce a non-routable address. The production path
// uses advertisedIP (from -ip flag), not localServerID, to prevent this.
t.Run("opaque_identity_not_routable", func(t *testing.T) {
vol3 := createTestVol(t)
defer vol3.Close()
// Pass an opaque identity string — NOT an IP address.
if err := vol3.StartReplicaReceiver(":0", ":0", "my-custom-server-id"); err != nil {
t.Fatal(err)
}
info := vol3.ReplicaReceiverAddr()
if info == nil {
t.Fatal("ReplicaReceiverAddr() returned nil")
}
// The address will be "my-custom-server-id:port" — NOT parseable as a routable IP.
ip := net.ParseIP(strings.Split(info.DataAddr, ":")[0])
if ip != nil {
t.Fatalf("opaque identity should NOT produce a parseable IP, got %s", info.DataAddr)
}
t.Logf("correctly non-routable: %s (production uses advertisedIP, not serverID)", info.DataAddr)
})
}
// --- Bug 2: LSN gap after shipper degradation ---