diff --git a/weed/storage/erasure_coding/distribution/analysis.go b/weed/storage/erasure_coding/distribution/analysis.go index d71d77fc3d..e19d4624a0 100644 --- a/weed/storage/erasure_coding/distribution/analysis.go +++ b/weed/storage/erasure_coding/distribution/analysis.go @@ -16,15 +16,21 @@ func NewTopologyAnalysis() *TopologyAnalysis { } // AddNode registers a node in the topology, including any shards it already -// holds for the volume being analyzed. A node id may only register once. +// holds for the volume being analyzed. A node id may only register once; +// registering it again updates its capacity and adds any new shards. The +// first registration wins for the node's place in the topology. 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) { - a.DCToRacks[node.DataCenter] = append(a.DCToRacks[node.DataCenter], node.Rack) + registered, ok := a.AllNodes[node.NodeID] + if !ok { + registered = node + 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) { + a.DCToRacks[node.DataCenter] = append(a.DCToRacks[node.DataCenter], node.Rack) + } + } else { + registered.FreeSlots = node.FreeSlots + registered.TotalShards = node.TotalShards } seen := node.ShardIDs[:0] for _, shardID := range node.ShardIDs { @@ -36,9 +42,9 @@ func (a *TopologyAnalysis) AddNode(node *TopologyNode) { for _, shardID := range node.ShardIDs { a.AddShardLocation(ShardLocation{ ShardID: shardID, - NodeID: node.NodeID, - DataCenter: node.DataCenter, - Rack: node.Rack, + NodeID: registered.NodeID, + DataCenter: registered.DataCenter, + Rack: registered.Rack, }) } } diff --git a/weed/storage/erasure_coding/distribution/distribution_test.go b/weed/storage/erasure_coding/distribution/distribution_test.go index ef73b8d814..f1179b28fe 100644 --- a/weed/storage/erasure_coding/distribution/distribution_test.go +++ b/weed/storage/erasure_coding/distribution/distribution_test.go @@ -157,6 +157,20 @@ func TestTopologyAnalysisDuplicateNode(t *testing.T) { assert.Equal(t, 2, a.ShardsByNode["n1"]) } +// A node auto-registered by a shard location gets its real capacity when the +// node itself registers later, plus any shards the location did not mention. +func TestTopologyAnalysisNodeCapacityLateRegistration(t *testing.T) { + a := NewTopologyAnalysis() + a.AddShardLocation(ShardLocation{ShardID: 0, NodeID: "n1", DataCenter: "dc1", Rack: "r1"}) + a.AddNode(makeNode("n1", "dc1", "r1", 10, 1)) + a.Finalize() + + assert.Equal(t, 10, a.AllNodes["n1"].FreeSlots) + assert.Equal(t, []int{0, 1}, a.AllNodes["n1"].ShardIDs) + assert.Equal(t, 2, a.ShardsByNode["n1"]) + assert.Len(t, a.RackToNodes[rackKey("dc1", "r1")], 1) +} + // Shards may be registered either through TopologyNode.ShardIDs or through // AddShardLocation — both must leave the node holding the shards. func TestTopologyAnalysisAddShardLocation(t *testing.T) {