From 2b71843a22e187622a0de6d83bd84a1ff68449ee Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Wed, 21 Oct 2026 14:11:12 -0700 Subject: [PATCH] ec distribution: never move a shard onto a node already holding it The move picker selected the destination node before the shard, so a node already holding the chosen shard could be targeted. Pick the shard first (parity-first) and choose among destination nodes that lack it. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- .../erasure_coding/distribution/rebalancer.go | 32 +++++++++++-------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/weed/storage/erasure_coding/distribution/rebalancer.go b/weed/storage/erasure_coding/distribution/rebalancer.go index 644a3e8221..8516ea7b04 100644 --- a/weed/storage/erasure_coding/distribution/rebalancer.go +++ b/weed/storage/erasure_coding/distribution/rebalancer.go @@ -85,26 +85,32 @@ func (r *Rebalancer) PlanRebalance(analysis *TopologyAnalysis) (*RebalancePlan, } moveOut := func(level, srcDomain string, srcNodes, dstNodes []*TopologyNode) bool { - var dst *TopologyNode - for _, n := range dstNodes { - if n.FreeSlots > 0 && (dst == nil || nodeLess(n, dst)) { - dst = n - } - } - if dst == nil { - return false - } var shardIDs []int for _, n := range srcNodes { shardIDs = append(shardIDs, n.ShardIDs...) } for _, shardID := range r.ecConfig.SortShardsParityFirst(shardIDs) { - for _, src := range srcNodes { - if contains(src.ShardIDs, shardID) { - recordMove(shardID, src, dst, level, srcDomain) - return true + var src *TopologyNode + for _, n := range srcNodes { + if contains(n.ShardIDs, shardID) { + src = n + break } } + if src == nil { + continue + } + var dst *TopologyNode + for _, n := range dstNodes { + if n.FreeSlots > 0 && !contains(n.ShardIDs, shardID) && (dst == nil || nodeLess(n, dst)) { + dst = n + } + } + if dst == nil { + continue + } + recordMove(shardID, src, dst, level, srcDomain) + return true } return false }