From 7012383c3f6f756940534b94e81b61120cf3e8a4 Mon Sep 17 00:00:00 2001 From: pingqiu Date: Fri, 3 Apr 2026 13:18:30 -0700 Subject: [PATCH] =?UTF-8?q?fix:=20StartReplicaReceiver=20idempotency=20gua?= =?UTF-8?q?rd=20=E2=80=94=20skip=20if=20already=20running?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit P0 bug on real hardware: assignments are re-delivered every heartbeat cycle (5s). First setupReplicaReceiver succeeds (receiver starts on deterministic port). Second call fails with "bind: address already in use" because the listener is already bound. The volume stays permanently degraded, blocking all RF=2 sync_all replication. Fix: skip StartReplicaReceiver if v.replRecv is already set. The receiver only needs to start once per volume lifetime. Co-Authored-By: Claude Opus 4.6 (1M context) --- weed/storage/blockvol/blockvol.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/weed/storage/blockvol/blockvol.go b/weed/storage/blockvol/blockvol.go index 57d9fa980..ac6520fc1 100644 --- a/weed/storage/blockvol/blockvol.go +++ b/weed/storage/blockvol/blockvol.go @@ -1304,6 +1304,13 @@ func (v *BlockVol) ReplicaReceiverAddr() *ReplicaReceiverAddrInfo { // advertisedHost:port instead of relying on outbound-IP fallback. On multi-NIC // hosts, always provide advertisedHost to ensure cross-machine reachability. func (v *BlockVol) StartReplicaReceiver(dataAddr, ctrlAddr string, advertisedHost ...string) error { + // Idempotency: skip if receiver is already running on this volume. + // Assignments are re-delivered on every heartbeat cycle; the receiver + // only needs to start once. Without this guard, the second Listen() + // fails with "bind: address already in use" and the volume stays degraded. + if v.replRecv != nil { + return nil + } recv, err := NewReplicaReceiver(v, dataAddr, ctrlAddr, advertisedHost...) if err != nil { return err