Some checks failed
go: build dev binaries / cleanup (push) Successful in 4s
Codespell / Check for spelling errors (push) Failing after 3s
docker: build dev containers / build-rust-binaries (amd64, x86_64-unknown-linux-musl) (push) Failing after 3s
docker: build dev containers / build-dev-containers (push) Has been skipped
docker: build dev containers / build-rust-binaries (arm64, true, aarch64-unknown-linux-musl) (push) Failing after 3s
End to End / FUSE Mount (push) Failing after 2s
EC Integration Tests / EC Integration Tests (push) Failing after 30s
FUSE DLM Integration Tests / FUSE DLM Integration Tests (push) Failing after 3s
FUSE Volume Server Failover Tests / FUSE Volume Server Failover (push) Failing after 3s
FUSE Integration Tests / FUSE Integration Testing (push) Failing after 2s
FUSE P2P Peer Chunk Sharing Integration Tests / FUSE P2P Peer Chunk Sharing (push) Failing after 3s
go: build binary / Go Vet (push) Failing after 2s
go: build binary / Go Vet 32-bit (push) Failing after 3s
go: build binary / Build (push) Failing after 4s
go: build binary / Build cross-platform (freebsd/amd64) (push) Failing after 3s
go: build binary / Build cross-platform (windows/amd64) (push) Failing after 3s
go: build binary / Build cross-platform (darwin/arm64) (push) Failing after 3s
go: build binary / Build cross-platform (windows/arm64) (push) Failing after 3s
go: build binary / Test (push) Failing after 2s
go: build binary / Test 32-bit (push) Failing after 3s
helm: lint and test charts / lint-test (push) Failing after 3s
Java Client Integration Tests / Java Integration Tests (11) (push) Failing after 3s
Java Client Integration Tests / Java Integration Tests (17) (push) Failing after 3s
go: build dev binaries / build_dev_linux_windows (amd64, linux) (push) Failing after 2s
go: build dev binaries / build_dev_linux_windows (amd64, windows) (push) Failing after 3s
go: build dev binaries / build_dev_darwin (amd64, darwin) (push) Has been skipped
go: build dev binaries / build_dev_darwin (arm64, darwin) (push) Has been skipped
Java Client Unit Tests / Java Unit Tests (11) (push) Failing after 3s
Java Client Unit Tests / Java Unit Tests (17) (push) Failing after 2s
Java Client Unit Tests / Java Unit Tests (21) (push) Failing after 3s
Java Client Unit Tests / Java Unit Tests (8) (push) Failing after 3s
Kafka Quick Test (Load Test with Schema Registry) / Kafka Client Load Test (Quick) (push) Failing after 3s
Kafka Gateway Tests / Kafka Unit Tests (unit-tests-1) (push) Failing after 11s
Kafka Gateway Tests / Kafka Integration Tests (Critical) (integration-1) (push) Failing after 3s
Kafka Gateway Tests / Kafka End-to-End Tests (with SMQ) (e2e-1) (push) Failing after 3s
Kafka Gateway Tests / Kafka Consumer Group Tests (Highly Isolated) (consumer-group-1) (push) Failing after 2s
Kafka Gateway Tests / Kafka Client Compatibility (with SMQ) (client-compat-1) (push) Failing after 3s
Kafka Gateway Tests / Kafka SMQ Integration Tests (Full Stack) (smq-integration-1) (push) Failing after 3s
Kafka Gateway Tests / Kafka Protocol Tests (Isolated) (protocol-1) (push) Failing after 3s
KMS Tests / KMS Provider Integration Tests (push) Failing after 2s
KMS Tests / S3 KMS End-to-End Tests (push) Failing after 3s
Master Cold Start Tests / Master Cold Start Tests (push) Failing after 4s
Metadata Subscribe Integration Tests / Metadata Subscribe Integration Tests (push) Failing after 3s
Multi-Master Tests / Multi-Master Failover Tests (push) Failing after 3s
Performance / CPU and Heap Profile (push) Failing after 2s
Performance / S3 Read/Write Benchmark (rust volume) (push) Failing after 3s
pjdfstest POSIX Compliance / pjdfstest (push) Failing after 2s
Plugin Worker Integration Tests / Plugin Worker: erasure_coding (push) Failing after 3s
Plugin Worker Integration Tests / Plugin Worker: vacuum (push) Failing after 3s
Rust Volume Server Tests / Rust Integration Tests (push) Failing after 3s
Rust Volume Server Tests / Go Tests with Rust Volume (grpc - Shard 1) (push) Failing after 2s
Rust Volume Server Tests / Go Tests with Rust Volume (http - Shard 1) (push) Failing after 3s
Rust Volume Server Tests / Go Tests with Rust Volume (grpc - Shard 2) (push) Failing after 3s
Rust Volume Server Tests / Go Tests with Rust Volume (http - Shard 2) (push) Failing after 3s
Rust Volume Server Tests / Go Tests with Rust Volume (grpc - Shard 3) (push) Failing after 3s
S3 IAM Integration Tests / S3 IAM Distributed Tests (push) Failing after 2s
S3 IAM Integration Tests / S3 IAM Performance Tests (push) Failing after 3s
Performance / Throughput Benchmark (go volume) (push) Failing after 3s
Performance / Throughput Benchmark (rust volume) (push) Failing after 2s
Performance / S3 Read/Write Benchmark (go volume) (push) Failing after 3s
Plugin Worker Integration Tests / Plugin Worker: volume_balance (push) Failing after 3s
PostgreSQL Gateway Tests / PostgreSQL Basic Tests (push) Failing after 3s
Rust Volume Server Tests / Rust Unit Tests (push) Failing after 2s
Rust Volume Server Tests / Go Tests with Rust Volume (http - Shard 3) (push) Failing after 3s
Rust Plugin Worker Tests / Rust Plugin Worker Build and Unit Tests (push) Failing after 3s
rust: build dev volume server binaries / cleanup (push) Successful in 3s
S3 ETag and ACL Tests / S3 ETag + ACL Integration Tests (push) Failing after 3s
S3 IAM Integration Tests / IAM Unit Tests (push) Failing after 3s
S3 IAM Integration Tests / S3 IAM Integration Tests (advanced) (push) Failing after 3s
S3 IAM Integration Tests / S3 IAM Integration Tests (basic) (push) Failing after 2s
S3 IAM Integration Tests / S3 IAM Integration Tests (group) (push) Failing after 3s
S3 IAM Integration Tests / S3 IAM Integration Tests (policy-enforcement) (push) Failing after 2s
S3 IAM Integration Tests / S3 IAM Integration Tests (sts) (push) Failing after 4s
S3 Keycloak Integration Tests / S3 Keycloak Integration Tests (push) Failing after 3s
S3 PyArrow Parquet Tests / PyArrow Parquet Tests (Python 3.11) (push) Failing after 3s
S3 PyArrow Parquet Tests / Go Unit Tests (Implicit Directory) (push) Has been cancelled
S3 PyArrow Parquet Tests / PyArrow Parquet Tests (Python 3.12) (push) Has been cancelled
rust: build dev volume server binaries / build-rust-volume-dev-darwin (darwin-amd64, x86_64-apple-darwin) (push) Has been cancelled
rust: build dev volume server binaries / build-rust-volume-dev-darwin (darwin-arm64, aarch64-apple-darwin) (push) Has been cancelled
rust: build dev volume server binaries / build-rust-volume-dev-linux (linux-amd64, x86_64-unknown-linux-gnu) (push) Has been cancelled
S3 PyArrow Parquet Tests / PyArrow Parquet Tests (Python 3.9) (push) Has been cancelled
S3 Proxy Signature Tests / S3 Proxy Signature Verification Tests (push) Has been cancelled
S3 SDK V2 Route Disambiguation Tests / S3 SDK V2 Routing Tests (push) Has been cancelled
S3 SSE Tests / S3 SSE Integration Tests (quick) (push) Has been cancelled
S3 SSE Tests / S3 SSE Compatibility Test (push) Has been cancelled
S3 SSE Tests / S3 SSE Metadata Persistence Test (push) Has been cancelled
S3 SSE Tests / S3 SSE Copy Operations Test (push) Has been cancelled
S3 SSE Tests / S3 SSE Multipart Upload Test (push) Has been cancelled
S3 SSE Tests / S3 SSE Performance Test (push) Has been cancelled
S3 SSE Tests / S3 Volume Encryption Test (push) Has been cancelled
S3 SSE Tests / S3 SSE Integration Tests (comprehensive) (push) Has been cancelled
Spark Integration Tests / Spark Integration Tests (push) Failing after 2s
Telemetry Integration Tests / telemetry-integration-test (push) Failing after 3s
Volume Server Integration Tests / Volume Server Integration Tests (grpc - Shard 1) (push) Failing after 3s
Volume Server Integration Tests / Volume Server Integration Tests (http - Shard 1) (push) Failing after 3s
Ceph S3 tests / Basic S3 tests (KV store) (push) Has been cancelled
Ceph S3 tests / S3 Versioning & Object Lock tests (push) Has been cancelled
S3 Policy Integration Tests / S3 Policy Unit Tests (push) Failing after 3s
Ceph S3 tests / S3 CORS tests (push) Has been cancelled
Ceph S3 tests / SeaweedFS Custom S3 Copy tests (push) Has been cancelled
Ceph S3 tests / Basic S3 tests (SQL store) (push) Has been cancelled
test s3 over https using aws-cli / awscli-tests (push) Has been cancelled
TLS Rotation Integration Tests / TLS Rotation Integration Tests (push) Has been cancelled
Vacuum Integration Tests / Vacuum Integration Tests (push) Has been cancelled
S3 Policy Integration Tests / S3 Policy Variables Integration Tests (push) Failing after 3s
S3 Policy Integration Tests / S3 Policy Enforcement Integration Tests (advanced-policy) (push) Failing after 3s
S3 Policy Integration Tests / S3 Policy Enforcement Integration Tests (basic-policy) (push) Failing after 2s
S3 Policy Integration Tests / S3 Policy Enforcement Integration Tests (contextual-policy) (push) Failing after 4s
S3 Policy Integration Tests / Trusted Proxy Detection Tests (push) Failing after 2s
S3 Policy Integration Tests / S3 Policy Shell Integration Tests (push) Failing after 3s
Samba on FUSE Integration / samba-integration (push) Failing after 2s
SFTP Integration Tests / SFTP Integration Testing (push) Failing after 3s
terraform: validate and test modules / fmt, validate, plan-level tests (push) Failing after 3s
terraform: validate and test modules / local cluster smoke test (real weed) (push) Failing after 2s
Volume Server Integration Tests / Volume Server Integration Tests (grpc - Shard 3) (push) Failing after 3s
Volume Server Integration Tests / Volume Server Integration Tests (grpc - Shard 2) (push) Failing after 3s
Volume Server Integration Tests / Volume Server Integration Tests (http - Shard 2) (push) Failing after 2s
Volume Server Integration Tests / Volume Server Integration Tests (http - Shard 3) (push) Failing after 5s
mount: windows conformance / WinFsp conformance (push) Has been cancelled
mount: windows / Mount on Windows (push) Has been cancelled
295 lines
9.3 KiB
Go
295 lines
9.3 KiB
Go
package weed_server
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"path/filepath"
|
|
"sort"
|
|
"testing"
|
|
|
|
"github.com/seaweedfs/seaweedfs/weed/pb/volume_server_pb"
|
|
"github.com/seaweedfs/seaweedfs/weed/stats"
|
|
"github.com/seaweedfs/seaweedfs/weed/storage"
|
|
"github.com/seaweedfs/seaweedfs/weed/storage/erasure_coding"
|
|
"github.com/seaweedfs/seaweedfs/weed/storage/needle"
|
|
"github.com/seaweedfs/seaweedfs/weed/storage/types"
|
|
"github.com/seaweedfs/seaweedfs/weed/storage/volume_info"
|
|
"github.com/seaweedfs/seaweedfs/weed/util"
|
|
)
|
|
|
|
func TestCheckEcVolumeStatusCountOnlyDataShards(t *testing.T) {
|
|
tempDir := t.TempDir()
|
|
dataDir := filepath.Join(tempDir, "data")
|
|
idxDir := filepath.Join(tempDir, "idx")
|
|
if err := os.MkdirAll(dataDir, 0o755); err != nil {
|
|
t.Fatalf("mkdir data dir: %v", err)
|
|
}
|
|
if err := os.MkdirAll(idxDir, 0o755); err != nil {
|
|
t.Fatalf("mkdir idx dir: %v", err)
|
|
}
|
|
|
|
baseName := "7"
|
|
filesToCreate := []string{
|
|
filepath.Join(dataDir, baseName+".ec00"),
|
|
filepath.Join(dataDir, baseName+".ec09"),
|
|
filepath.Join(dataDir, baseName+".ec13"),
|
|
filepath.Join(idxDir, baseName+".ecx"),
|
|
filepath.Join(idxDir, baseName+".ecj"),
|
|
filepath.Join(idxDir, baseName+".idx"),
|
|
}
|
|
for _, fileName := range filesToCreate {
|
|
if err := os.WriteFile(fileName, []byte("x"), 0o644); err != nil {
|
|
t.Fatalf("create %s: %v", fileName, err)
|
|
}
|
|
}
|
|
|
|
location := &storage.DiskLocation{
|
|
Directory: dataDir,
|
|
IdxDirectory: idxDir,
|
|
}
|
|
|
|
hasEcxFile, hasIdxFile, shardCount, err := checkEcVolumeStatus(baseName, location)
|
|
if err != nil {
|
|
t.Fatalf("checkEcVolumeStatus: %v", err)
|
|
}
|
|
|
|
if !hasEcxFile {
|
|
t.Fatalf("expected hasEcxFile=true")
|
|
}
|
|
if !hasIdxFile {
|
|
t.Fatalf("expected hasIdxFile=true")
|
|
}
|
|
if shardCount != 3 {
|
|
t.Fatalf("expected shardCount=3, got %d", shardCount)
|
|
}
|
|
}
|
|
|
|
// TestRemoveStaleEcArtifacts: a fresh encode deletes every prior EC artifact
|
|
// (incl. shard ids beyond the default ratio and versioned bitrot sidecars)
|
|
// while leaving the source .dat/.idx/.vif untouched.
|
|
func TestRemoveStaleEcArtifacts(t *testing.T) {
|
|
tempDir := t.TempDir()
|
|
dataDir := filepath.Join(tempDir, "data")
|
|
idxDir := filepath.Join(tempDir, "idx")
|
|
for _, d := range []string{dataDir, idxDir} {
|
|
if err := os.MkdirAll(d, 0o755); err != nil {
|
|
t.Fatalf("mkdir %s: %v", d, err)
|
|
}
|
|
}
|
|
|
|
const baseName = "7"
|
|
dataBase := filepath.Join(dataDir, baseName)
|
|
idxBase := filepath.Join(idxDir, baseName)
|
|
|
|
// EC artifacts that must be removed, including a shard id past the default
|
|
// 10+4 ratio (proves the MaxShardCount scan) and a versioned sidecar.
|
|
var ecFiles []string
|
|
for _, id := range []int{0, 9, 13, 20, erasure_coding.MaxShardCount - 1} {
|
|
ecFiles = append(ecFiles, dataBase+erasure_coding.ToExt(id))
|
|
}
|
|
ecFiles = append(ecFiles,
|
|
dataBase+".ecx", dataBase+".ecj",
|
|
idxBase+".ecx", idxBase+".ecj",
|
|
dataBase+erasure_coding.BitrotSidecarExt, // .ecsum (generation 0)
|
|
dataBase+erasure_coding.BitrotSidecarExt+".v2", // versioned sidecar
|
|
)
|
|
|
|
// Source files that must survive — the authoritative input for the encode.
|
|
srcFiles := []string{dataBase + ".dat", idxBase + ".idx", dataBase + ".vif"}
|
|
|
|
for _, f := range append(append([]string{}, ecFiles...), srcFiles...) {
|
|
if err := os.WriteFile(f, []byte("x"), 0o644); err != nil {
|
|
t.Fatalf("create %s: %v", f, err)
|
|
}
|
|
}
|
|
|
|
removeStaleEcArtifacts(dataBase, idxBase, erasure_coding.MaxShardCount)
|
|
|
|
for _, f := range ecFiles {
|
|
if util.FileExists(f) {
|
|
t.Errorf("expected EC artifact removed: %s", f)
|
|
}
|
|
}
|
|
for _, f := range srcFiles {
|
|
if !util.FileExists(f) {
|
|
t.Errorf("expected source file preserved: %s", f)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestDeleteEcShardsWithoutLocalEcx: the delete handler removes the requested
|
|
// shard files even on a disk with no local .ecx, so a failed-copy orphan stays
|
|
// cleanable rather than being mounted later under a foreign index.
|
|
func TestDeleteEcShardsWithoutLocalEcx(t *testing.T) {
|
|
tempDir := t.TempDir()
|
|
dataDir := filepath.Join(tempDir, "data")
|
|
idxDir := filepath.Join(tempDir, "idx")
|
|
for _, d := range []string{dataDir, idxDir} {
|
|
if err := os.MkdirAll(d, 0o755); err != nil {
|
|
t.Fatalf("mkdir %s: %v", d, err)
|
|
}
|
|
}
|
|
|
|
const baseName = "7"
|
|
// Orphan shard files with NO .ecx/.idx anywhere — a failed-copy leftover.
|
|
orphans := []string{
|
|
filepath.Join(dataDir, baseName+".ec03"),
|
|
filepath.Join(dataDir, baseName+".ec11"),
|
|
}
|
|
for _, f := range orphans {
|
|
if err := os.WriteFile(f, []byte("x"), 0o644); err != nil {
|
|
t.Fatalf("create %s: %v", f, err)
|
|
}
|
|
}
|
|
|
|
location := &storage.DiskLocation{Directory: dataDir, IdxDirectory: idxDir}
|
|
if err := deleteEcShardIdsForEachLocation(baseName, location, []*storage.DiskLocation{location}, []uint32{3, 11}); err != nil {
|
|
t.Fatalf("deleteEcShardIdsForEachLocation: %v", err)
|
|
}
|
|
for _, f := range orphans {
|
|
if util.FileExists(f) {
|
|
t.Errorf("expected orphan shard removed without a local .ecx: %s", f)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestVolumeEcShardsInfo_AggregatesAcrossDisks pins the multi-disk path:
|
|
// when a volume server mounts EC shards for the same volume on more than
|
|
// one disk (each disk holds its own EcVolume entry — Store.FindEcVolume
|
|
// returns only the first), VolumeEcShardsInfo used to report shards from
|
|
// a single disk. The ec.encode verification step (verifyEcShardsBeforeDelete)
|
|
// then refused to delete the source volume because the union across
|
|
// servers fell short of dataShards + parityShards. The handler must walk
|
|
// every DiskLocation so the response covers every shard the server holds.
|
|
func TestVolumeEcShardsInfo_AggregatesAcrossDisks(t *testing.T) {
|
|
tempDir := t.TempDir()
|
|
dir0 := filepath.Join(tempDir, "disk0")
|
|
dir1 := filepath.Join(tempDir, "disk1")
|
|
for _, d := range []string{dir0, dir1} {
|
|
if err := os.MkdirAll(d, 0o755); err != nil {
|
|
t.Fatalf("mkdir %s: %v", d, err)
|
|
}
|
|
}
|
|
|
|
const collection = "ec-multi-disk-info"
|
|
vid := needle.VolumeId(42)
|
|
const dataShards, parityShards = 10, 4
|
|
const datSize int64 = 10 * 1024 * 1024
|
|
|
|
// Two shards on disk0, two on disk1. The .ecx / .ecj / .vif live on
|
|
// disk0 so each disk's EcVolume can open the index files via the
|
|
// cross-disk fallback in NewEcVolume.
|
|
shardsOnDisk0 := []erasure_coding.ShardId{0, 5}
|
|
shardsOnDisk1 := []erasure_coding.ShardId{7, 12}
|
|
diskIOProbeConfig := stats.DefaultDiskIOProbeConfig()
|
|
|
|
store := storage.NewStore(nil, "localhost", 8080, 18080, "http://localhost:8080", "store-id",
|
|
[]string{dir0, dir1},
|
|
[]int32{100, 100},
|
|
[]util.MinFreeSpace{{}, {}},
|
|
"",
|
|
storage.NeedleMapInMemory,
|
|
[]types.DiskType{types.HardDriveType, types.HardDriveType},
|
|
nil,
|
|
3,
|
|
diskIOProbeConfig,
|
|
)
|
|
done := make(chan struct{})
|
|
go func() {
|
|
for {
|
|
select {
|
|
case <-store.NewEcShardsChan:
|
|
case <-store.NewVolumesChan:
|
|
case <-store.DeletedVolumesChan:
|
|
case <-store.DeletedEcShardsChan:
|
|
case <-store.StateUpdateChan:
|
|
case <-done:
|
|
return
|
|
}
|
|
}
|
|
}()
|
|
t.Cleanup(func() {
|
|
store.Close()
|
|
close(done)
|
|
})
|
|
|
|
base0 := erasure_coding.EcShardFileName(collection, dir0, int(vid))
|
|
base1 := erasure_coding.EcShardFileName(collection, dir1, int(vid))
|
|
|
|
// .ecx, .ecj, .vif live on disk0. NewEcVolume on disk1 falls back to
|
|
// disk0's idx dir. The .ecx needs >0 bytes so HasEcxFileOnDisk does
|
|
// not treat it as the corrupt-stub case; a single zero entry is the
|
|
// smallest valid index file (WalkIndex iterates one zero-sized needle).
|
|
if err := os.WriteFile(base0+".ecx", make([]byte, 16), 0o644); err != nil {
|
|
t.Fatalf("write .ecx: %v", err)
|
|
}
|
|
if err := os.WriteFile(base0+".ecj", nil, 0o644); err != nil {
|
|
t.Fatalf("write .ecj: %v", err)
|
|
}
|
|
if err := volume_info.SaveVolumeInfo(base0+".vif", &volume_server_pb.VolumeInfo{
|
|
Version: uint32(needle.Version3),
|
|
DatFileSize: datSize,
|
|
EcShardConfig: &volume_server_pb.EcShardConfig{
|
|
DataShards: dataShards,
|
|
ParityShards: parityShards,
|
|
},
|
|
}); err != nil {
|
|
t.Fatalf("save .vif: %v", err)
|
|
}
|
|
|
|
plant := func(base string, shardId erasure_coding.ShardId) {
|
|
t.Helper()
|
|
f, err := os.Create(base + erasure_coding.ToExt(int(shardId)))
|
|
if err != nil {
|
|
t.Fatalf("create shard %d: %v", shardId, err)
|
|
}
|
|
// MountEcShards does not validate shard size, so any non-empty
|
|
// truncate avoids the zero-byte ignore branch in loadAllEcShards.
|
|
if err := f.Truncate(1); err != nil {
|
|
f.Close()
|
|
t.Fatalf("truncate shard %d: %v", shardId, err)
|
|
}
|
|
f.Close()
|
|
}
|
|
for _, sid := range shardsOnDisk0 {
|
|
plant(base0, sid)
|
|
}
|
|
for _, sid := range shardsOnDisk1 {
|
|
plant(base1, sid)
|
|
}
|
|
|
|
for _, sid := range append([]erasure_coding.ShardId{}, append(shardsOnDisk0, shardsOnDisk1...)...) {
|
|
if err := store.MountEcShards(collection, vid, sid, ""); err != nil {
|
|
t.Fatalf("MountEcShards %d.%d: %v", vid, sid, err)
|
|
}
|
|
}
|
|
|
|
vs := &VolumeServer{store: store}
|
|
resp, err := vs.VolumeEcShardsInfo(context.Background(), &volume_server_pb.VolumeEcShardsInfoRequest{
|
|
VolumeId: uint32(vid),
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("VolumeEcShardsInfo: %v", err)
|
|
}
|
|
|
|
gotShardIds := make([]int, 0, len(resp.GetEcShardInfos()))
|
|
for _, info := range resp.GetEcShardInfos() {
|
|
if info.GetVolumeId() != uint32(vid) {
|
|
t.Errorf("EcShardInfo VolumeId=%d, want %d", info.GetVolumeId(), vid)
|
|
}
|
|
gotShardIds = append(gotShardIds, int(info.GetShardId()))
|
|
}
|
|
sort.Ints(gotShardIds)
|
|
|
|
want := []int{0, 5, 7, 12}
|
|
if len(gotShardIds) != len(want) {
|
|
t.Fatalf("VolumeEcShardsInfo returned %d shards (ids=%v), want %d (ids=%v)",
|
|
len(gotShardIds), gotShardIds, len(want), want)
|
|
}
|
|
for i, sid := range want {
|
|
if gotShardIds[i] != sid {
|
|
t.Fatalf("VolumeEcShardsInfo shard ids=%v, want %v", gotShardIds, want)
|
|
}
|
|
}
|
|
}
|