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
331 lines
9.6 KiB
Go
331 lines
9.6 KiB
Go
package dash
|
||
|
||
import (
|
||
"testing"
|
||
|
||
"github.com/seaweedfs/seaweedfs/weed/pb/master_pb"
|
||
)
|
||
|
||
// TestCollectCollectionStatsECUnevenShards verifies that EC shards spread
|
||
// across multiple disks are aggregated correctly, including the case where
|
||
// the last data shard is smaller than the rest.
|
||
func TestCollectCollectionStatsECUnevenShards(t *testing.T) {
|
||
// Standard 10+4 EC layout. Shards 0..9 are data, 10..13 are parity.
|
||
// Data shards 0..8 are 1000 bytes; data shard 9 is 500 bytes (uneven tail).
|
||
// Parity shards 10..13 are 1000 bytes each.
|
||
//
|
||
// Physical (raw) = 9*1000 + 500 + 4*1000 = 13500
|
||
// Logical (data only) = 9*1000 + 500 = 9500
|
||
|
||
nodeA := &master_pb.DataNodeInfo{
|
||
DiskInfos: map[string]*master_pb.DiskInfo{
|
||
"disk1": {
|
||
EcShardInfos: []*master_pb.VolumeEcShardInformationMessage{
|
||
{
|
||
Id: 42,
|
||
Collection: "bucket-a",
|
||
// Shards 0..6 held here.
|
||
EcIndexBits: (1 << 0) | (1 << 1) | (1 << 2) | (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6),
|
||
ShardSizes: []int64{1000, 1000, 1000, 1000, 1000, 1000, 1000},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
}
|
||
nodeB := &master_pb.DataNodeInfo{
|
||
DiskInfos: map[string]*master_pb.DiskInfo{
|
||
"disk1": {
|
||
EcShardInfos: []*master_pb.VolumeEcShardInformationMessage{
|
||
{
|
||
Id: 42,
|
||
Collection: "bucket-a",
|
||
// Shards 7..13 held here. Shard 9 (data) is the short tail.
|
||
EcIndexBits: (1 << 7) | (1 << 8) | (1 << 9) | (1 << 10) | (1 << 11) | (1 << 12) | (1 << 13),
|
||
ShardSizes: []int64{1000, 1000, 500, 1000, 1000, 1000, 1000},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
}
|
||
|
||
topo := &master_pb.TopologyInfo{
|
||
DataCenterInfos: []*master_pb.DataCenterInfo{
|
||
{
|
||
RackInfos: []*master_pb.RackInfo{
|
||
{
|
||
DataNodeInfos: []*master_pb.DataNodeInfo{nodeA, nodeB},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
}
|
||
|
||
stats := collectCollectionStats(topo)
|
||
|
||
got, ok := stats["bucket-a"]
|
||
if !ok {
|
||
t.Fatalf("expected collection bucket-a in stats, got: %v", stats)
|
||
}
|
||
|
||
const wantPhysical int64 = 13500
|
||
const wantLogical int64 = 9500
|
||
|
||
if got.PhysicalSize != wantPhysical {
|
||
t.Errorf("PhysicalSize: got %d, want %d", got.PhysicalSize, wantPhysical)
|
||
}
|
||
if got.LogicalSize != wantLogical {
|
||
t.Errorf("LogicalSize: got %d, want %d", got.LogicalSize, wantLogical)
|
||
}
|
||
}
|
||
|
||
// TestCollectCollectionStatsECEmptyCollection verifies that EC shards with
|
||
// an empty collection name are bucketed under "default".
|
||
func TestCollectCollectionStatsECEmptyCollection(t *testing.T) {
|
||
topo := &master_pb.TopologyInfo{
|
||
DataCenterInfos: []*master_pb.DataCenterInfo{
|
||
{
|
||
RackInfos: []*master_pb.RackInfo{
|
||
{
|
||
DataNodeInfos: []*master_pb.DataNodeInfo{
|
||
{
|
||
DiskInfos: map[string]*master_pb.DiskInfo{
|
||
"disk1": {
|
||
EcShardInfos: []*master_pb.VolumeEcShardInformationMessage{
|
||
{
|
||
Id: 1,
|
||
Collection: "",
|
||
EcIndexBits: (1 << 0) | (1 << 10),
|
||
ShardSizes: []int64{2000, 2000},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
}
|
||
|
||
stats := collectCollectionStats(topo)
|
||
|
||
got, ok := stats["default"]
|
||
if !ok {
|
||
t.Fatalf("expected collection default in stats, got: %v", stats)
|
||
}
|
||
if got.PhysicalSize != 4000 {
|
||
t.Errorf("PhysicalSize: got %d, want 4000", got.PhysicalSize)
|
||
}
|
||
// Only shard 0 is a data shard; shard 10 is parity.
|
||
if got.LogicalSize != 2000 {
|
||
t.Errorf("LogicalSize: got %d, want 2000", got.LogicalSize)
|
||
}
|
||
}
|
||
|
||
// TestCollectCollectionStatsECFileAndDeleteCountAggregation verifies that
|
||
// FileCount for an EC volume is deduped across nodes (every shard holder has
|
||
// an identical .ecx, so the total entry count is taken once) while
|
||
// DeleteCount is summed (each needle delete tombstones exactly one node's
|
||
// .ecx, so the true delete total is the sum of every holder's local count).
|
||
func TestCollectCollectionStatsECFileAndDeleteCountAggregation(t *testing.T) {
|
||
// Volume id=7 reported by three nodes. Every node reports file_count=100
|
||
// (same .ecx). Local delete counts: 5 + 3 + 2 = 10 deletes total.
|
||
// Expected live object count = 100 - 10 = 90.
|
||
makeNode := func(bits uint32, sizes []int64, deleteCount uint64) *master_pb.DataNodeInfo {
|
||
return &master_pb.DataNodeInfo{
|
||
DiskInfos: map[string]*master_pb.DiskInfo{
|
||
"disk1": {
|
||
EcShardInfos: []*master_pb.VolumeEcShardInformationMessage{
|
||
{
|
||
Id: 7,
|
||
Collection: "bucket-a",
|
||
EcIndexBits: bits,
|
||
ShardSizes: sizes,
|
||
FileCount: 100,
|
||
DeleteCount: deleteCount,
|
||
},
|
||
},
|
||
},
|
||
},
|
||
}
|
||
}
|
||
|
||
topo := &master_pb.TopologyInfo{
|
||
DataCenterInfos: []*master_pb.DataCenterInfo{
|
||
{
|
||
RackInfos: []*master_pb.RackInfo{
|
||
{
|
||
DataNodeInfos: []*master_pb.DataNodeInfo{
|
||
makeNode((1<<0)|(1<<1)|(1<<2)|(1<<3), []int64{1000, 1000, 1000, 1000}, 5),
|
||
makeNode((1<<4)|(1<<5)|(1<<6)|(1<<7), []int64{1000, 1000, 1000, 1000}, 3),
|
||
makeNode((1<<8)|(1<<9)|(1<<10)|(1<<11)|(1<<12)|(1<<13), []int64{1000, 1000, 1000, 1000, 1000, 1000}, 2),
|
||
},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
}
|
||
|
||
stats := collectCollectionStats(topo)
|
||
|
||
got, ok := stats["bucket-a"]
|
||
if !ok {
|
||
t.Fatalf("expected collection bucket-a in stats, got: %v", stats)
|
||
}
|
||
if got.FileCount != 90 {
|
||
t.Errorf("FileCount: got %d, want 90 (100 total - 10 deletes summed)", got.FileCount)
|
||
}
|
||
// Sanity: 14 shards × 1000 bytes physical, 10 data shards logical.
|
||
if got.PhysicalSize != 14000 {
|
||
t.Errorf("PhysicalSize: got %d, want 14000", got.PhysicalSize)
|
||
}
|
||
if got.LogicalSize != 10000 {
|
||
t.Errorf("LogicalSize: got %d, want 10000", got.LogicalSize)
|
||
}
|
||
}
|
||
|
||
// TestCollectCollectionStatsECFileCountMaxDedupe verifies that EC file_count
|
||
// is taken as the max across reporting nodes rather than the first-seen
|
||
// value. A node that has not yet finished loading .ecx reports file_count=0,
|
||
// which previously poisoned the aggregate and rendered buckets backed by EC
|
||
// volumes as "0 objects".
|
||
func TestCollectCollectionStatsECFileCountMaxDedupe(t *testing.T) {
|
||
makeNode := func(bits uint32, sizes []int64, fileCount uint64) *master_pb.DataNodeInfo {
|
||
return &master_pb.DataNodeInfo{
|
||
DiskInfos: map[string]*master_pb.DiskInfo{
|
||
"disk1": {
|
||
EcShardInfos: []*master_pb.VolumeEcShardInformationMessage{
|
||
{
|
||
Id: 11,
|
||
Collection: "bucket-b",
|
||
EcIndexBits: bits,
|
||
ShardSizes: sizes,
|
||
FileCount: fileCount,
|
||
},
|
||
},
|
||
},
|
||
},
|
||
}
|
||
}
|
||
|
||
topo := &master_pb.TopologyInfo{
|
||
DataCenterInfos: []*master_pb.DataCenterInfo{
|
||
{
|
||
RackInfos: []*master_pb.RackInfo{
|
||
{
|
||
// First-reporting node has a stale fileCount of 0,
|
||
// second node reports the authoritative 6.
|
||
DataNodeInfos: []*master_pb.DataNodeInfo{
|
||
makeNode((1<<0)|(1<<1)|(1<<2)|(1<<3)|(1<<4)|(1<<5)|(1<<6), []int64{1, 1, 1, 1, 1, 1, 1}, 0),
|
||
makeNode((1<<7)|(1<<8)|(1<<9)|(1<<10)|(1<<11)|(1<<12)|(1<<13), []int64{1, 1, 1, 1, 1, 1, 1}, 6),
|
||
},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
}
|
||
|
||
stats := collectCollectionStats(topo)
|
||
got, ok := stats["bucket-b"]
|
||
if !ok {
|
||
t.Fatalf("expected collection bucket-b in stats, got: %v", stats)
|
||
}
|
||
if got.FileCount != 6 {
|
||
t.Errorf("FileCount: got %d, want 6 (max across reporters)", got.FileCount)
|
||
}
|
||
}
|
||
|
||
// TestTotalCollectionFileCount verifies the cluster-wide chunk count counts a
|
||
// chunk once per replicated volume and per EC volume, and nets out deletes.
|
||
func TestTotalCollectionFileCount(t *testing.T) {
|
||
// Volume 1 has replication 001 (two copies), so both nodes report the same
|
||
// 101 chunks with 10 deleted: 91 live chunks in total, not 182 and not the
|
||
// 90 that halving each report would give.
|
||
replica := func() *master_pb.DiskInfo {
|
||
return &master_pb.DiskInfo{
|
||
VolumeInfos: []*master_pb.VolumeInformationMessage{
|
||
{
|
||
Id: 1,
|
||
Collection: "bucket-a",
|
||
ReplicaPlacement: 1,
|
||
FileCount: 101,
|
||
DeleteCount: 10,
|
||
},
|
||
},
|
||
}
|
||
}
|
||
// EC volume 2 has its 20 chunks reported by every shard holder.
|
||
ecShards := func(bits uint32, sizes []int64) *master_pb.DiskInfo {
|
||
return &master_pb.DiskInfo{
|
||
EcShardInfos: []*master_pb.VolumeEcShardInformationMessage{
|
||
{
|
||
Id: 2,
|
||
Collection: "bucket-b",
|
||
EcIndexBits: bits,
|
||
ShardSizes: sizes,
|
||
FileCount: 20,
|
||
},
|
||
},
|
||
}
|
||
}
|
||
|
||
topo := &master_pb.TopologyInfo{
|
||
DataCenterInfos: []*master_pb.DataCenterInfo{
|
||
{
|
||
RackInfos: []*master_pb.RackInfo{
|
||
{
|
||
DataNodeInfos: []*master_pb.DataNodeInfo{
|
||
{DiskInfos: map[string]*master_pb.DiskInfo{
|
||
"disk1": replica(),
|
||
"disk2": ecShards((1<<0)|(1<<1), []int64{1, 1}),
|
||
}},
|
||
{DiskInfos: map[string]*master_pb.DiskInfo{
|
||
"disk1": replica(),
|
||
"disk2": ecShards((1<<2)|(1<<3), []int64{1, 1}),
|
||
}},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
}
|
||
|
||
if got := totalCollectionFileCount(topo); got != 111 {
|
||
t.Errorf("totalCollectionFileCount: got %d, want 111 (91 replicated + 20 EC)", got)
|
||
}
|
||
}
|
||
|
||
// TestTotalCollectionFileCountUnderReplicated verifies a volume whose second
|
||
// replica has not reported yet still contributes its full live count.
|
||
func TestTotalCollectionFileCountUnderReplicated(t *testing.T) {
|
||
topo := &master_pb.TopologyInfo{
|
||
DataCenterInfos: []*master_pb.DataCenterInfo{
|
||
{
|
||
RackInfos: []*master_pb.RackInfo{
|
||
{
|
||
DataNodeInfos: []*master_pb.DataNodeInfo{
|
||
{DiskInfos: map[string]*master_pb.DiskInfo{
|
||
"disk1": {
|
||
VolumeInfos: []*master_pb.VolumeInformationMessage{
|
||
{
|
||
Id: 1,
|
||
Collection: "bucket-a",
|
||
ReplicaPlacement: 1,
|
||
FileCount: 50,
|
||
},
|
||
},
|
||
},
|
||
}},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
},
|
||
}
|
||
|
||
if got := totalCollectionFileCount(topo); got != 50 {
|
||
t.Errorf("totalCollectionFileCount: got %d, want 50 (one replica reporting, not halved)", got)
|
||
}
|
||
}
|