diff --git a/weed/shell/command_ec_rebuild.go b/weed/shell/command_ec_rebuild.go index 396a42b1f..dc1b6e6cc 100644 --- a/weed/shell/command_ec_rebuild.go +++ b/weed/shell/command_ec_rebuild.go @@ -333,7 +333,15 @@ func (erb *ecRebuilder) prepareDataToRecover(rebuilder *EcNode, collection strin } } - for i, ecNodes := range locations { + targetShardCount := erasure_coding.TotalShardsCount + for i := erasure_coding.TotalShardsCount; i < len(locations); i++ { + if len(locations[i]) > 0 { + targetShardCount = i + 1 + } + } + + for i := 0; i < targetShardCount; i++ { + ecNodes := locations[i] shardId := erasure_coding.ShardId(i) if len(ecNodes) == 0 { erb.write("missing shard %d.%d\n", volumeId, shardId) diff --git a/weed/shell/command_ec_rebuild_test.go b/weed/shell/command_ec_rebuild_test.go index 4b076564b..3baaee325 100644 --- a/weed/shell/command_ec_rebuild_test.go +++ b/weed/shell/command_ec_rebuild_test.go @@ -2,6 +2,7 @@ package shell import ( "bytes" + "fmt" "strings" "testing" @@ -257,3 +258,50 @@ func TestDuplicateShards(t *testing.T) { t.Errorf("Expected 7 unique shards, got %d", count) } } + +// TestPrepareDataToRecoverTargetShardCount tests that prepareDataToRecover caps the shard reporting +func TestPrepareDataToRecoverTargetShardCount(t *testing.T) { + var logBuffer bytes.Buffer + + // Create a node with 10 shards (0-9) + node1 := newEcNode("dc1", "rack1", "node1", 100). + addEcVolumeAndShardsForTest(1, "c1", []erasure_coding.ShardId{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}) + + erb := &ecRebuilder{ + commandEnv: &CommandEnv{ + env: make(map[string]string), + noLock: true, + }, + ecNodes: []*EcNode{node1}, + writer: &logBuffer, + } + + locations := make(EcShardLocations, erasure_coding.MaxShardCount) + for i := 0; i < 10; i++ { + locations[i] = []*EcNode{node1} + } + + // Shards 10-13 are missing, but 14-31 should NOT be reported + _, _, err := erb.prepareDataToRecover(node1, "c1", needle.VolumeId(1), locations) + if err != nil { + t.Fatalf("prepareDataToRecover failed: %v", err) + } + + output := logBuffer.String() + lines := strings.Split(strings.TrimSpace(output), "\n") + + // We expect "use existing shard 1.0" through "use existing shard 1.9" (10 lines) + // and "missing shard 1.10" through "missing shard 1.13" (4 lines) + // Total 14 lines expected. Shards 14-31 should be absent. + expectedLineCount := 14 + if len(lines) != expectedLineCount { + t.Errorf("Expected %d lines of output, got %d. Output:\n%s", expectedLineCount, len(lines), output) + } + + for i := 14; i < erasure_coding.MaxShardCount; i++ { + if strings.Contains(output, "shard 1."+strings.TrimSpace(string(rune('0'+i)))) || + strings.Contains(output, "shard 1."+fmt.Sprintf("%d", i)) { + t.Errorf("Shard 1.%d should not be reported in output", i) + } + } +}