ec distribution: merge repeat node registrations

A shard location now auto-registers an unknown node with no free slots;
a later AddNode carrying the real capacity was then dropped entirely.
Repeat registration now updates the node's capacity and adopts any new
shards, while the first registration keeps the topology placement.
This commit is contained in:
Devin
2026-10-21 14:58:34 -07:00
parent 09c8b65e5b
commit d4b8318cc3
2 changed files with 31 additions and 11 deletions
@@ -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,
})
}
}
@@ -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) {