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
310 lines
12 KiB
Go
310 lines
12 KiB
Go
package topology
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/seaweedfs/seaweedfs/weed/pb/master_pb"
|
|
"github.com/seaweedfs/seaweedfs/weed/storage/erasure_coding"
|
|
)
|
|
|
|
// referenceCollectionStatistics is what callers computed for themselves from a
|
|
// full topology listing, kept here so the summary can be held to producing the
|
|
// same numbers. Quota enforcement reads these.
|
|
func referenceCollectionStatistics(t *master_pb.TopologyInfo) map[string]*CollectionStatistics {
|
|
type volumeKey struct {
|
|
collection string
|
|
volumeId uint32
|
|
}
|
|
out := map[string]*CollectionStatistics{}
|
|
seen := map[volumeKey]bool{}
|
|
ecCounts := map[volumeKey]*ecFileCounts{}
|
|
statsFor := func(c string) *CollectionStatistics {
|
|
s, ok := out[c]
|
|
if !ok {
|
|
s = &CollectionStatistics{Collection: c}
|
|
out[c] = s
|
|
}
|
|
return s
|
|
}
|
|
|
|
for _, dc := range t.DataCenterInfos {
|
|
for _, r := range dc.RackInfos {
|
|
for _, dn := range r.DataNodeInfos {
|
|
for _, diskInfo := range dn.DiskInfos {
|
|
for _, vi := range diskInfo.VolumeInfos {
|
|
s := statsFor(vi.Collection)
|
|
s.PhysicalSize += vi.Size
|
|
key := volumeKey{vi.Collection, vi.Id}
|
|
if seen[key] {
|
|
continue
|
|
}
|
|
seen[key] = true
|
|
s.Size += vi.Size
|
|
s.FileCount += vi.FileCount
|
|
s.DeleteCount += vi.DeleteCount
|
|
s.DeletedByteCount += vi.DeletedByteCount
|
|
s.VolumeCount++
|
|
}
|
|
for _, esi := range diskInfo.EcShardInfos {
|
|
s := statsFor(esi.Collection)
|
|
s.PhysicalSize += uint64(erasure_coding.EcShardsTotalSize(esi))
|
|
s.Size += uint64(erasure_coding.EcShardsDataSize(esi, 0))
|
|
key := volumeKey{esi.Collection, esi.Id}
|
|
agg, ok := ecCounts[key]
|
|
if !ok {
|
|
agg = &ecFileCounts{collection: esi.Collection}
|
|
ecCounts[key] = agg
|
|
s.VolumeCount++
|
|
}
|
|
if esi.FileCount > agg.fileCount {
|
|
agg.fileCount = esi.FileCount
|
|
}
|
|
agg.deleteCount += esi.DeleteCount
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for _, agg := range ecCounts {
|
|
if s := out[agg.collection]; s != nil {
|
|
s.FileCount += agg.fileCount
|
|
s.DeleteCount += agg.deleteCount
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
func statsTopology(t *testing.T) *Topology {
|
|
t.Helper()
|
|
topo := NewTopology("stats", nil, 32*1024*1024*1024, 5, false)
|
|
rack := topo.GetOrCreateDataCenter("dc1").GetOrCreateRack("rack1")
|
|
nodes := make([]*DataNode, 3)
|
|
for i := range nodes {
|
|
nodes[i] = rack.GetOrCreateDataNode(
|
|
"10.0.0."+string(rune('1'+i)), 8080, 18080, "", "", map[string]uint32{"": 1000, "ssd": 1000})
|
|
}
|
|
|
|
// Two collections, replicated volumes, one volume on a second disk type.
|
|
// Replicas agree here; disagreement is covered on its own, where the
|
|
// listing this is compared against is itself order dependent.
|
|
report := func(dn *DataNode, msgs ...*master_pb.VolumeInformationMessage) {
|
|
topo.SyncDataNodeRegistration(msgs, dn)
|
|
}
|
|
vol := func(id uint32, collection string, size uint64, diskType string) *master_pb.VolumeInformationMessage {
|
|
return &master_pb.VolumeInformationMessage{
|
|
Id: id, Collection: collection, Size: size, FileCount: uint64(id) * 10,
|
|
DeleteCount: uint64(id), DeletedByteCount: uint64(id) * 100,
|
|
Version: 3, ReplicaPlacement: 1, DiskType: diskType,
|
|
}
|
|
}
|
|
report(nodes[0], vol(1, "bucket-a", 1000, ""), vol(2, "bucket-a", 2000, ""), vol(5, "bucket-b", 700, "ssd"))
|
|
report(nodes[1], vol(1, "bucket-a", 1000, ""), vol(3, "bucket-b", 3000, ""))
|
|
report(nodes[2], vol(2, "bucket-a", 2000, ""), vol(3, "bucket-b", 3000, ""))
|
|
|
|
// Ec shards for a third collection, spread over two nodes, with the file
|
|
// count reported differently by each holder.
|
|
topo.SyncDataNodeEcShards([]*master_pb.VolumeEcShardInformationMessage{
|
|
{Id: 9, Collection: "bucket-c", EcIndexBits: 0x1f, ShardSizes: []int64{10, 20, 30, 40, 50}, FileCount: 40, DeleteCount: 2},
|
|
}, nodes[0])
|
|
topo.SyncDataNodeEcShards([]*master_pb.VolumeEcShardInformationMessage{
|
|
{Id: 9, Collection: "bucket-c", EcIndexBits: 0x3e0, ShardSizes: []int64{60, 70, 80, 90, 100}, FileCount: 44, DeleteCount: 3},
|
|
}, nodes[1])
|
|
return topo
|
|
}
|
|
|
|
// The summary replaces callers adding up a full topology listing, so it has to
|
|
// produce what that produced: these numbers enforce bucket quotas.
|
|
func TestCollectionStatisticsMatchesAFullListing(t *testing.T) {
|
|
topo := statsTopology(t)
|
|
|
|
want := referenceCollectionStatistics(topo.ToTopologyInfo(VolumeFilter{}))
|
|
got := map[string]*CollectionStatistics{}
|
|
for _, s := range topo.CollectionStatistics() {
|
|
got[s.Collection] = s
|
|
}
|
|
|
|
if len(got) != len(want) {
|
|
t.Fatalf("summarised %d collections, the listing had %d: %v vs %v", len(got), len(want), got, want)
|
|
}
|
|
for name, expected := range want {
|
|
actual, found := got[name]
|
|
if !found {
|
|
t.Errorf("collection %s is missing from the summary", name)
|
|
continue
|
|
}
|
|
if *actual != *expected {
|
|
t.Errorf("collection %s:\n summary %+v\n listing %+v", name, *actual, *expected)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Replicas disagree while a write is landing or a heartbeat is late. The full
|
|
// listing walked the topology in map order and took whichever replica it
|
|
// reached first, so its answer was not stable; the summary takes the largest.
|
|
func TestCollectionStatisticsPicksTheLargestReplica(t *testing.T) {
|
|
topo := NewTopology("stats", nil, 32*1024*1024*1024, 5, false)
|
|
rack := topo.GetOrCreateDataCenter("dc1").GetOrCreateRack("rack1")
|
|
small := rack.GetOrCreateDataNode("10.0.0.1", 8080, 18080, "", "a", map[string]uint32{"": 100})
|
|
large := rack.GetOrCreateDataNode("10.0.0.2", 8080, 18080, "", "b", map[string]uint32{"": 100})
|
|
|
|
behind := &master_pb.VolumeInformationMessage{
|
|
Id: 1, Collection: "bucket-a", Size: 1000, FileCount: 10, Version: 3, ReplicaPlacement: 1,
|
|
}
|
|
ahead := &master_pb.VolumeInformationMessage{
|
|
Id: 1, Collection: "bucket-a", Size: 4000, FileCount: 40, Version: 3, ReplicaPlacement: 1,
|
|
}
|
|
topo.SyncDataNodeRegistration([]*master_pb.VolumeInformationMessage{behind}, small)
|
|
topo.SyncDataNodeRegistration([]*master_pb.VolumeInformationMessage{ahead}, large)
|
|
|
|
for i := 0; i < 8; i++ {
|
|
stats := topo.CollectionStatistics()
|
|
if len(stats) != 1 {
|
|
t.Fatalf("expected one collection, got %d", len(stats))
|
|
}
|
|
if stats[0].Size != 4000 || stats[0].FileCount != 40 {
|
|
t.Fatalf("run %d reported size %d files %d, want the larger replica's 4000 and 40",
|
|
i, stats[0].Size, stats[0].FileCount)
|
|
}
|
|
if stats[0].PhysicalSize != 5000 {
|
|
t.Fatalf("run %d reported physical size %d, want both replicas summed", i, stats[0].PhysicalSize)
|
|
}
|
|
}
|
|
}
|
|
|
|
func statsByCollection(topo *Topology) map[string]*CollectionStatistics {
|
|
out := map[string]*CollectionStatistics{}
|
|
for _, s := range topo.CollectionStatistics() {
|
|
out[s.Collection] = s
|
|
}
|
|
return out
|
|
}
|
|
|
|
// The .ecx and .ecj travel with the shards, so a holder still loading them
|
|
// reports zero and must not pin the volume's count down.
|
|
func TestCollectionStatisticsTakesTheLargestEcFileCount(t *testing.T) {
|
|
topo := NewTopology("stats", nil, 32*1024*1024*1024, 5, false)
|
|
rack := topo.GetOrCreateDataCenter("dc1").GetOrCreateRack("rack1")
|
|
loading := rack.GetOrCreateDataNode("10.0.0.1", 8080, 18080, "", "a", map[string]uint32{"": 100})
|
|
loaded := rack.GetOrCreateDataNode("10.0.0.2", 8080, 18080, "", "b", map[string]uint32{"": 100})
|
|
|
|
topo.SyncDataNodeEcShards([]*master_pb.VolumeEcShardInformationMessage{
|
|
{Id: 11, Collection: "bucket-b", EcIndexBits: 0x7f, ShardSizes: []int64{1, 1, 1, 1, 1, 1, 1}, FileCount: 0},
|
|
}, loading)
|
|
topo.SyncDataNodeEcShards([]*master_pb.VolumeEcShardInformationMessage{
|
|
{Id: 11, Collection: "bucket-b", EcIndexBits: 0x3f80, ShardSizes: []int64{1, 1, 1, 1, 1, 1, 1}, FileCount: 6},
|
|
}, loaded)
|
|
|
|
stats := statsByCollection(topo)["bucket-b"]
|
|
if stats == nil {
|
|
t.Fatal("expected bucket-b to be reported")
|
|
}
|
|
if stats.FileCount != 6 {
|
|
t.Errorf("file count %d, want the largest any holder reported (6)", stats.FileCount)
|
|
}
|
|
if stats.VolumeCount != 1 {
|
|
t.Errorf("volume count %d, want one volume however many holders report shards", stats.VolumeCount)
|
|
}
|
|
// 14 shards of 1 byte each; only the 10 data shards count as logical.
|
|
if stats.PhysicalSize != 14 {
|
|
t.Errorf("physical size %d, want every shard counted (14)", stats.PhysicalSize)
|
|
}
|
|
if stats.Size != 10 {
|
|
t.Errorf("size %d, want the data shards only (10)", stats.Size)
|
|
}
|
|
}
|
|
|
|
// A collection with both kinds has to have them added together.
|
|
func TestCollectionStatisticsCountsRegularAndEcTogether(t *testing.T) {
|
|
topo := NewTopology("stats", nil, 32*1024*1024*1024, 5, false)
|
|
rack := topo.GetOrCreateDataCenter("dc1").GetOrCreateRack("rack1")
|
|
dn := rack.GetOrCreateDataNode("10.0.0.1", 8080, 18080, "", "a", map[string]uint32{"": 100})
|
|
|
|
topo.SyncDataNodeRegistration([]*master_pb.VolumeInformationMessage{
|
|
{Id: 1, Collection: "mixed", Size: 500, FileCount: 5, DeleteCount: 1, DeletedByteCount: 50, Version: 3},
|
|
}, dn)
|
|
topo.SyncDataNodeEcShards([]*master_pb.VolumeEcShardInformationMessage{
|
|
{Id: 2, Collection: "mixed", EcIndexBits: 0x3fff,
|
|
ShardSizes: []int64{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, FileCount: 7, DeleteCount: 2},
|
|
}, dn)
|
|
|
|
stats := statsByCollection(topo)["mixed"]
|
|
if stats == nil {
|
|
t.Fatal("expected the mixed collection to be reported")
|
|
}
|
|
if stats.VolumeCount != 2 {
|
|
t.Errorf("volume count %d, want the regular and the ec volume (2)", stats.VolumeCount)
|
|
}
|
|
if stats.FileCount != 12 {
|
|
t.Errorf("file count %d, want 5 regular plus 7 ec", stats.FileCount)
|
|
}
|
|
if stats.DeleteCount != 3 {
|
|
t.Errorf("delete count %d, want 1 regular plus 2 ec", stats.DeleteCount)
|
|
}
|
|
if stats.Size != 510 {
|
|
t.Errorf("size %d, want 500 regular plus 10 data shards", stats.Size)
|
|
}
|
|
if stats.PhysicalSize != 514 {
|
|
t.Errorf("physical size %d, want 500 regular plus all 14 shards", stats.PhysicalSize)
|
|
}
|
|
}
|
|
|
|
// Quotas are enforced on size less deletions, so the replica holding the most
|
|
// live data is the one to count. The replica with the biggest raw size can be
|
|
// the one that has deleted the most, and counting that one would report a
|
|
// bucket smaller than it is and leave it writable over its quota.
|
|
func TestCollectionStatisticsPicksTheReplicaHoldingTheMostLiveData(t *testing.T) {
|
|
topo := NewTopology("stats", nil, 32*1024*1024*1024, 5, false)
|
|
rack := topo.GetOrCreateDataCenter("dc1").GetOrCreateRack("rack1")
|
|
bigMostlyDeleted := rack.GetOrCreateDataNode("10.0.0.1", 8080, 18080, "", "a", map[string]uint32{"": 100})
|
|
smallerButLive := rack.GetOrCreateDataNode("10.0.0.2", 8080, 18080, "", "b", map[string]uint32{"": 100})
|
|
|
|
topo.SyncDataNodeRegistration([]*master_pb.VolumeInformationMessage{{
|
|
Id: 1, Collection: "bucket-a", Size: 1000, DeletedByteCount: 900,
|
|
Version: 3, ReplicaPlacement: 1,
|
|
}}, bigMostlyDeleted)
|
|
topo.SyncDataNodeRegistration([]*master_pb.VolumeInformationMessage{{
|
|
Id: 1, Collection: "bucket-a", Size: 900, DeletedByteCount: 0,
|
|
Version: 3, ReplicaPlacement: 1,
|
|
}}, smallerButLive)
|
|
|
|
stats := statsByCollection(topo)["bucket-a"]
|
|
if stats == nil {
|
|
t.Fatal("expected bucket-a to be reported")
|
|
}
|
|
live := stats.Size - stats.DeletedByteCount
|
|
if live != 900 {
|
|
t.Errorf("reported %d bytes live (size %d less %d deleted), want the 900 one replica holds",
|
|
live, stats.Size, stats.DeletedByteCount)
|
|
}
|
|
if stats.PhysicalSize != 1900 {
|
|
t.Errorf("physical size %d, want both replicas summed", stats.PhysicalSize)
|
|
}
|
|
}
|
|
|
|
// Live usage is read as the collection's size less its deletions, so a volume
|
|
// reporting more deleted bytes than it holds must not cancel live bytes
|
|
// belonging to other volumes in the same bucket.
|
|
func TestCollectionStatisticsDeletionsNeverExceedTheVolume(t *testing.T) {
|
|
topo := NewTopology("stats", nil, 32*1024*1024*1024, 5, false)
|
|
dn := topo.GetOrCreateDataCenter("dc1").GetOrCreateRack("rack1").
|
|
GetOrCreateDataNode("10.0.0.1", 8080, 18080, "", "a", map[string]uint32{"": 100})
|
|
|
|
topo.SyncDataNodeRegistration([]*master_pb.VolumeInformationMessage{
|
|
// More deleted bytes than the volume holds, which a compaction can
|
|
// leave behind, and a healthy volume beside it.
|
|
{Id: 1, Collection: "bucket-a", Size: 100, DeletedByteCount: 500, Version: 3},
|
|
{Id: 2, Collection: "bucket-a", Size: 1000, DeletedByteCount: 0, Version: 3},
|
|
}, dn)
|
|
|
|
stats := statsByCollection(topo)["bucket-a"]
|
|
if stats == nil {
|
|
t.Fatal("expected bucket-a to be reported")
|
|
}
|
|
if stats.DeletedByteCount > stats.Size {
|
|
t.Errorf("deletions %d exceed size %d, so live usage reads as zero", stats.DeletedByteCount, stats.Size)
|
|
}
|
|
if live := stats.Size - stats.DeletedByteCount; live != 1000 {
|
|
t.Errorf("reported %d bytes live, want the 1000 the second volume holds", live)
|
|
}
|
|
}
|