diff --git a/weed/shell/command_ec_common.go b/weed/shell/command_ec_common.go index ce69b19ee..f2cc581da 100644 --- a/weed/shell/command_ec_common.go +++ b/weed/shell/command_ec_common.go @@ -436,13 +436,29 @@ func collectEcVolumeServersByDc(topo *master_pb.TopologyInfo, selectedDataCenter disks: make(map[uint32]*EcDisk), } - // Build disk-level information from EC shard info + // Build disk-level information from volumes and EC shards + // First, discover all unique disk IDs from VolumeInfos (includes empty disks) + allDiskIds := make(map[uint32]string) // diskId -> diskType for diskType, diskInfo := range dn.DiskInfos { if diskInfo == nil { continue } - // Group EC shards by disk_id - diskShards := make(map[uint32]map[needle.VolumeId]erasure_coding.ShardBits) + // Get all disk IDs from volumes + for _, vi := range diskInfo.VolumeInfos { + allDiskIds[vi.DiskId] = diskType + } + // Also get disk IDs from EC shards + for _, ecShardInfo := range diskInfo.EcShardInfos { + allDiskIds[ecShardInfo.DiskId] = diskType + } + } + + // Group EC shards by disk_id + diskShards := make(map[uint32]map[needle.VolumeId]erasure_coding.ShardBits) + for _, diskInfo := range dn.DiskInfos { + if diskInfo == nil { + continue + } for _, ecShardInfo := range diskInfo.EcShardInfos { diskId := ecShardInfo.DiskId if diskShards[diskId] == nil { @@ -451,32 +467,31 @@ func collectEcVolumeServersByDc(topo *master_pb.TopologyInfo, selectedDataCenter vid := needle.VolumeId(ecShardInfo.Id) diskShards[diskId][vid] = erasure_coding.ShardBits(ecShardInfo.EcIndexBits) } + } + + // Create EcDisk for each discovered disk + diskCount := len(allDiskIds) + if diskCount == 0 { + diskCount = 1 + } + freePerDisk := int(freeEcSlots) / diskCount - // If no EC shards, still create disk entry based on DiskInfo.DiskId - if len(diskShards) == 0 && diskInfo.DiskId > 0 { - diskShards[diskInfo.DiskId] = make(map[needle.VolumeId]erasure_coding.ShardBits) + for diskId, diskType := range allDiskIds { + shards := diskShards[diskId] + if shards == nil { + shards = make(map[needle.VolumeId]erasure_coding.ShardBits) + } + totalShardCount := 0 + for _, shardBits := range shards { + totalShardCount += shardBits.ShardIdCount() } - // Create EcDisk for each disk_id found - for diskId, shards := range diskShards { - totalShardCount := 0 - for _, shardBits := range shards { - totalShardCount += shardBits.ShardIdCount() - } - // Estimate free slots per disk (simplified: divide evenly if multiple disks) - diskCount := len(diskShards) - if diskCount == 0 { - diskCount = 1 - } - freePerDisk := int(freeEcSlots) / diskCount - - ecNode.disks[diskId] = &EcDisk{ - diskId: diskId, - diskType: diskType, - freeEcSlots: freePerDisk, - ecShardCount: totalShardCount, - ecShards: shards, - } + ecNode.disks[diskId] = &EcDisk{ + diskId: diskId, + diskType: diskType, + freeEcSlots: freePerDisk, + ecShardCount: totalShardCount, + ecShards: shards, } } diff --git a/weed/shell/command_volume_server_evacuate.go b/weed/shell/command_volume_server_evacuate.go index 0ddee0b51..6135eb3eb 100644 --- a/weed/shell/command_volume_server_evacuate.go +++ b/weed/shell/command_volume_server_evacuate.go @@ -199,7 +199,11 @@ func (c *commandVolumeServerEvacuate) moveAwayOneEcVolume(commandEnv *CommandEnv } vid := needle.VolumeId(ecShardInfo.Id) destDiskId := pickBestDiskOnNode(emptyNode, vid) - fmt.Fprintf(os.Stdout, "moving ec volume %s%d.%d %s => %s\n", collectionPrefix, ecShardInfo.Id, shardId, thisNode.info.Id, emptyNode.info.Id) + if destDiskId > 0 { + fmt.Fprintf(os.Stdout, "moving ec volume %s%d.%d %s => %s (disk %d)\n", collectionPrefix, ecShardInfo.Id, shardId, thisNode.info.Id, emptyNode.info.Id, destDiskId) + } else { + fmt.Fprintf(os.Stdout, "moving ec volume %s%d.%d %s => %s\n", collectionPrefix, ecShardInfo.Id, shardId, thisNode.info.Id, emptyNode.info.Id) + } err = moveMountedShardToEcNode(commandEnv, thisNode, ecShardInfo.Collection, vid, shardId, emptyNode, destDiskId, applyChange) if err != nil { return diff --git a/weed/storage/erasure_coding/placement/placement.go b/weed/storage/erasure_coding/placement/placement.go index 4ed99fe8e..67e21c1f8 100644 --- a/weed/storage/erasure_coding/placement/placement.go +++ b/weed/storage/erasure_coding/placement/placement.go @@ -114,8 +114,6 @@ func SelectDestinations(disks []*DiskCandidate, config PlacementRequest) (*Place // Build indexes for efficient lookup rackToDisks := groupDisksByRack(suitable) - serverToDisks := groupDisksByServer(suitable) - _ = serverToDisks // Used for reference result := &PlacementResult{ SelectedDisks: make([]*DiskCandidate, 0, config.ShardsNeeded), @@ -130,7 +128,7 @@ func SelectDestinations(disks []*DiskCandidate, config PlacementRequest) (*Place // Pass 1: Select one disk from each rack (maximize rack diversity) if config.PreferDifferentRacks { - // Sort racks by number of available servers (ascending) to prioritize underutilized racks + // Sort racks by number of available servers (descending) to prioritize racks with more options sortedRacks := sortRacksByServerCount(rackToDisks) for _, rackKey := range sortedRacks { if len(result.SelectedDisks) >= config.ShardsNeeded { diff --git a/weed/worker/tasks/erasure_coding/detection.go b/weed/worker/tasks/erasure_coding/detection.go index 5f13000b1..c5568fe26 100644 --- a/weed/worker/tasks/erasure_coding/detection.go +++ b/weed/worker/tasks/erasure_coding/detection.go @@ -477,10 +477,15 @@ func diskInfosToCandidates(disks []*topology.DiskInfo) []*placement.DiskCandidat freeSlots = 0 } - // Calculate EC shard count from EcShardInfos slice + // Calculate EC shard count for this specific disk + // EcShardInfos contains all shards, so we need to filter by DiskId and sum actual shard counts ecShardCount := 0 if disk.DiskInfo.EcShardInfos != nil { - ecShardCount = len(disk.DiskInfo.EcShardInfos) + for _, shardInfo := range disk.DiskInfo.EcShardInfos { + if shardInfo.DiskId == disk.DiskID { + ecShardCount += erasure_coding.ShardBits(shardInfo.EcIndexBits).ShardIdCount() + } + } } candidates = append(candidates, &placement.DiskCandidate{