From f3fcf9a06e38bdef6cd37f46c358716446fde31b Mon Sep 17 00:00:00 2001 From: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Wed, 21 Oct 2026 14:29:49 -0700 Subject: [PATCH] ec distribution: register each node id once Adding the same node twice appended it to RackToNodes twice, so every count that walks rack nodes double-counted its shards. --- weed/storage/erasure_coding/distribution/analysis.go | 5 ++++- .../erasure_coding/distribution/distribution_test.go | 12 ++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/weed/storage/erasure_coding/distribution/analysis.go b/weed/storage/erasure_coding/distribution/analysis.go index 2a7c98b820..5f60efda56 100644 --- a/weed/storage/erasure_coding/distribution/analysis.go +++ b/weed/storage/erasure_coding/distribution/analysis.go @@ -16,8 +16,11 @@ func NewTopologyAnalysis() *TopologyAnalysis { } // AddNode registers a node in the topology, including any shards it already -// holds for the volume being analyzed. +// holds for the volume being analyzed. A node id may only register once. func (a *TopologyAnalysis) AddNode(node *TopologyNode) { + if _, ok := a.AllNodes[node.NodeID]; ok { + return + } a.AllNodes[node.NodeID] = node a.RackToNodes[rackKey(node.DataCenter, node.Rack)] = append(a.RackToNodes[rackKey(node.DataCenter, node.Rack)], node) if !contains(a.DCToRacks[node.DataCenter], node.Rack) { diff --git a/weed/storage/erasure_coding/distribution/distribution_test.go b/weed/storage/erasure_coding/distribution/distribution_test.go index 574bab7b06..367807ac10 100644 --- a/weed/storage/erasure_coding/distribution/distribution_test.go +++ b/weed/storage/erasure_coding/distribution/distribution_test.go @@ -125,6 +125,18 @@ func TestTopologyAnalysis(t *testing.T) { assert.Equal(t, []int{0, 1, 2}, a.RackToShards[rackKey("dc1", "r1")]) } +// A node registered twice must still count once in rack and shard totals. +func TestTopologyAnalysisDuplicateNode(t *testing.T) { + a := NewTopologyAnalysis() + a.AddNode(makeNode("n1", "dc1", "r1", 5, 0, 1)) + a.AddNode(makeNode("n1", "dc1", "r1", 5, 0, 1)) + a.Finalize() + + assert.Equal(t, 1, a.TotalNodes) + assert.Len(t, a.RackToNodes[rackKey("dc1", "r1")], 1) + assert.Equal(t, 2, a.ShardsByNode["n1"]) +} + // Shards may be registered either through TopologyNode.ShardIDs or through // AddShardLocation — both must leave the node holding the shards. func TestTopologyAnalysisAddShardLocation(t *testing.T) {