From 38626b6a56dbf2981778095c1e1505aaee870103 Mon Sep 17 00:00:00 2001 From: chrislu Date: Mon, 27 Oct 2025 20:50:32 -0700 Subject: [PATCH] fix: replace TotalShardsCount with MaxShardCount for custom EC ratio support Critical fixes to support custom EC ratios > 14 shards: disk_location_ec.go: - validateEcVolume: Check shards 0-31 instead of 0-13 during validation - removeEcVolumeFiles: Remove shards 0-31 instead of 0-13 during cleanup ec_volume_info.go ShardBits methods: - ShardIds(): Iterate up to MaxShardCount (32) instead of TotalShardsCount (14) - ToUint32Slice(): Iterate up to MaxShardCount (32) - IndexToShardId(): Iterate up to MaxShardCount (32) - MinusParityShards(): Remove shards 10-31 instead of 10-13 (added note about Phase 2) - Minus() shard size copy: Iterate up to MaxShardCount (32) - resizeShardSizes(): Iterate up to MaxShardCount (32) Without these changes: - Custom EC ratios > 14 total shards would fail validation on startup - Shards 14-31 would never be discovered or cleaned up - ShardBits operations would miss shards >= 14 These changes are backward compatible - MaxShardCount (32) includes the default TotalShardsCount (14), so existing 10+4 volumes work as before. --- weed/storage/disk_location_ec.go | 9 +++++---- weed/storage/erasure_coding/ec_volume_info.go | 14 ++++++++------ 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/weed/storage/disk_location_ec.go b/weed/storage/disk_location_ec.go index 128bfd26f..de8eca9f3 100644 --- a/weed/storage/disk_location_ec.go +++ b/weed/storage/disk_location_ec.go @@ -398,8 +398,8 @@ func (l *DiskLocation) validateEcVolume(collection string, vid needle.VolumeId) var actualShardSize int64 = -1 // Count shards and validate they all have the same size (required for Reed-Solomon EC) - // Shard files (.ec00 - .ec13) are always in l.Directory, not l.IdxDirectory - for i := 0; i < erasure_coding.TotalShardsCount; i++ { + // Check up to MaxShardCount (32) to support custom EC ratios + for i := 0; i < erasure_coding.MaxShardCount; i++ { shardFileName := baseFileName + erasure_coding.ToExt(i) fi, err := os.Stat(shardFileName) @@ -472,8 +472,9 @@ func (l *DiskLocation) removeEcVolumeFiles(collection string, vid needle.VolumeI removeFile(indexBaseFileName+".ecx", "EC index file") removeFile(indexBaseFileName+".ecj", "EC journal file") - // Remove all EC shard files (.ec00 ~ .ec13) from data directory - for i := 0; i < erasure_coding.TotalShardsCount; i++ { + // Remove all EC shard files (.ec00 ~ .ec31) from data directory + // Use MaxShardCount (32) to support custom EC ratios + for i := 0; i < erasure_coding.MaxShardCount; i++ { removeFile(baseFileName+erasure_coding.ToExt(i), "EC shard file") } } diff --git a/weed/storage/erasure_coding/ec_volume_info.go b/weed/storage/erasure_coding/ec_volume_info.go index cd196084a..c2f51c112 100644 --- a/weed/storage/erasure_coding/ec_volume_info.go +++ b/weed/storage/erasure_coding/ec_volume_info.go @@ -87,7 +87,7 @@ func (ecInfo *EcVolumeInfo) Minus(other *EcVolumeInfo) *EcVolumeInfo { // Copy shard sizes for remaining shards retIndex := 0 - for shardId := ShardId(0); shardId < TotalShardsCount && retIndex < len(ret.ShardSizes); shardId++ { + for shardId := ShardId(0); shardId < MaxShardCount && retIndex < len(ret.ShardSizes); shardId++ { if ret.ShardBits.HasShardId(shardId) { if size, exists := ecInfo.GetShardSize(shardId); exists { ret.ShardSizes[retIndex] = size @@ -135,7 +135,7 @@ func (b ShardBits) HasShardId(id ShardId) bool { } func (b ShardBits) ShardIds() (ret []ShardId) { - for i := ShardId(0); i < TotalShardsCount; i++ { + for i := ShardId(0); i < MaxShardCount; i++ { if b.HasShardId(i) { ret = append(ret, i) } @@ -144,7 +144,7 @@ func (b ShardBits) ShardIds() (ret []ShardId) { } func (b ShardBits) ToUint32Slice() (ret []uint32) { - for i := uint32(0); i < TotalShardsCount; i++ { + for i := uint32(0); i < MaxShardCount; i++ { if b.HasShardId(ShardId(i)) { ret = append(ret, i) } @@ -168,7 +168,9 @@ func (b ShardBits) Plus(other ShardBits) ShardBits { } func (b ShardBits) MinusParityShards() ShardBits { - for i := DataShardsCount; i < TotalShardsCount; i++ { + // Note: This method assumes default 10+4 EC layout where parity shards are IDs 10-31 + // For custom EC ratios in Phase 2, this would need to accept an ECContext parameter + for i := DataShardsCount; i < MaxShardCount; i++ { b = b.RemoveShardId(ShardId(i)) } return b @@ -209,7 +211,7 @@ func (b ShardBits) IndexToShardId(index int) (shardId ShardId, found bool) { } currentIndex := 0 - for i := ShardId(0); i < TotalShardsCount; i++ { + for i := ShardId(0); i < MaxShardCount; i++ { if b.HasShardId(i) { if currentIndex == index { return i, true @@ -238,7 +240,7 @@ func (ecInfo *EcVolumeInfo) resizeShardSizes(prevShardBits ShardBits) { // Copy existing sizes to new positions based on current ShardBits if len(ecInfo.ShardSizes) > 0 { newIndex := 0 - for shardId := ShardId(0); shardId < TotalShardsCount && newIndex < expectedLength; shardId++ { + for shardId := ShardId(0); shardId < MaxShardCount && newIndex < expectedLength; shardId++ { if ecInfo.ShardBits.HasShardId(shardId) { // Try to find the size for this shard in the old array using previous ShardBits if oldIndex, found := prevShardBits.ShardIdToIndex(shardId); found && oldIndex < len(ecInfo.ShardSizes) {