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.7 KiB
Go
331 lines
9.7 KiB
Go
package wdclient
|
|
|
|
import (
|
|
"runtime"
|
|
"sync"
|
|
"testing"
|
|
|
|
"github.com/seaweedfs/seaweedfs/weed/pb"
|
|
)
|
|
|
|
func urlsOf(locations []Location) []string {
|
|
urls := make([]string, 0, len(locations))
|
|
for _, loc := range locations {
|
|
urls = append(urls, loc.Url)
|
|
}
|
|
return urls
|
|
}
|
|
|
|
// A volume that moved while we were talking to the previous master must answer
|
|
// with where it is now, not with both servers: the first write of a generation
|
|
// replaces the entry instead of merging into it.
|
|
func TestAddLocationReplacesEarlierGeneration(t *testing.T) {
|
|
vm := newVidMap("", DefaultVidMapCacheSize)
|
|
vid := uint32(3)
|
|
movedFrom := Location{Url: "10.0.0.1:8080"}
|
|
movedTo := Location{Url: "10.0.0.2:8080"}
|
|
|
|
vm.addLocation(vid, movedFrom)
|
|
vm.reset()
|
|
vm.addLocation(vid, movedTo)
|
|
|
|
locs, found := vm.GetLocations(vid)
|
|
if !found || len(locs) != 1 || locs[0].Url != movedTo.Url {
|
|
t.Fatalf("expected only %s after the move, got %v", movedTo.Url, urlsOf(locs))
|
|
}
|
|
if vm.hasVolumeServer(pb.ServerAddress(movedFrom.Url)) {
|
|
t.Errorf("server %s should no longer be referenced after the volume moved", movedFrom.Url)
|
|
}
|
|
if !vm.hasVolumeServer(pb.ServerAddress(movedTo.Url)) {
|
|
t.Errorf("server %s should be referenced after the volume moved", movedTo.Url)
|
|
}
|
|
}
|
|
|
|
// Replicas reported within one generation accumulate, and a repeated report of
|
|
// the same server is not counted twice.
|
|
func TestAddLocationMergesWithinGeneration(t *testing.T) {
|
|
vm := newVidMap("", DefaultVidMapCacheSize)
|
|
vid := uint32(4)
|
|
first := Location{Url: "10.0.0.1:8080"}
|
|
second := Location{Url: "10.0.0.2:8080"}
|
|
|
|
vm.addLocation(vid, first)
|
|
vm.addLocation(vid, second)
|
|
vm.addLocation(vid, first)
|
|
|
|
locs, found := vm.GetLocations(vid)
|
|
if !found || len(locs) != 2 {
|
|
t.Fatalf("expected both replicas, got %v", urlsOf(locs))
|
|
}
|
|
|
|
// One delete must be enough to evict the server: the duplicate report of
|
|
// `first` must not have taken a second reference.
|
|
vm.deleteLocation(vid, first)
|
|
if vm.hasVolumeServer(pb.ServerAddress(first.Url)) {
|
|
t.Errorf("server %s should be evicted after a single delete", first.Url)
|
|
}
|
|
}
|
|
|
|
// An entry survives exactly retainGenerations resets without being relearned.
|
|
func TestResetRetainsUntilWindowExpires(t *testing.T) {
|
|
const retain = 2
|
|
vm := newVidMap("", retain)
|
|
vid := uint32(5)
|
|
location := Location{Url: "10.0.0.1:8080"}
|
|
vm.addLocation(vid, location)
|
|
|
|
for i := 0; i < retain; i++ {
|
|
vm.reset()
|
|
if _, found := vm.GetLocations(vid); !found {
|
|
t.Fatalf("location should still be retained after %d reset(s)", i+1)
|
|
}
|
|
}
|
|
|
|
vm.reset()
|
|
if _, found := vm.GetLocations(vid); found {
|
|
t.Errorf("location should be dropped after %d resets", retain+1)
|
|
}
|
|
if vm.hasVolumeServer(pb.ServerAddress(location.Url)) {
|
|
t.Errorf("expiry should release the server reference for %s", location.Url)
|
|
}
|
|
if len(vm.serverRefCount) != 0 {
|
|
t.Errorf("expiry left %d dangling server refcounts", len(vm.serverRefCount))
|
|
}
|
|
}
|
|
|
|
// Relearning an entry restarts its retention window.
|
|
func TestResetWindowRestartsOnRelearn(t *testing.T) {
|
|
const retain = 2
|
|
vm := newVidMap("", retain)
|
|
vid := uint32(6)
|
|
location := Location{Url: "10.0.0.1:8080"}
|
|
|
|
for i := 0; i < retain+3; i++ {
|
|
vm.addLocation(vid, location)
|
|
vm.reset()
|
|
}
|
|
|
|
if _, found := vm.GetLocations(vid); !found {
|
|
t.Error("a location relearned every generation must never expire")
|
|
}
|
|
}
|
|
|
|
// A delete names one location; it must not make the rest of a stale entry look
|
|
// freshly learned and so outlive its retention window.
|
|
func TestDeleteLocationDoesNotRefreshGeneration(t *testing.T) {
|
|
const retain = 2
|
|
vm := newVidMap("", retain)
|
|
vid := uint32(7)
|
|
first := Location{Url: "10.0.0.1:8080"}
|
|
second := Location{Url: "10.0.0.2:8080"}
|
|
|
|
vm.addLocation(vid, first)
|
|
vm.addLocation(vid, second)
|
|
for i := 0; i < retain; i++ {
|
|
vm.reset()
|
|
}
|
|
vm.deleteLocation(vid, first)
|
|
|
|
locs, found := vm.GetLocations(vid)
|
|
if !found || len(locs) != 1 || locs[0].Url != second.Url {
|
|
t.Fatalf("expected %s to remain, got found=%v %v", second.Url, found, urlsOf(locs))
|
|
}
|
|
|
|
vm.reset()
|
|
if _, found := vm.GetLocations(vid); found {
|
|
t.Error("the surviving location was learned in the expired generation and should be dropped")
|
|
}
|
|
}
|
|
|
|
// Encoding a volume must stop the regular copies a previous generation knew
|
|
// from answering for it, and decoding it must stop its shards from answering.
|
|
func TestNewestGenerationWinsAcrossEcTransition(t *testing.T) {
|
|
regular := Location{Url: "10.0.0.1:8080"}
|
|
ecShard := Location{Url: "10.0.0.2:8080"}
|
|
|
|
t.Run("encoded", func(t *testing.T) {
|
|
vm := newVidMap("", DefaultVidMapCacheSize)
|
|
vm.addLocation(1, regular)
|
|
vm.reset()
|
|
vm.addEcLocation(1, ecShard)
|
|
|
|
locs, found := vm.GetLocations(1)
|
|
if !found || len(locs) != 1 || locs[0].Url != ecShard.Url {
|
|
t.Fatalf("expected the freshly learned EC shard, got found=%v %v", found, urlsOf(locs))
|
|
}
|
|
})
|
|
|
|
t.Run("decoded", func(t *testing.T) {
|
|
vm := newVidMap("", DefaultVidMapCacheSize)
|
|
vm.addEcLocation(1, ecShard)
|
|
vm.reset()
|
|
vm.addLocation(1, regular)
|
|
|
|
locs, found := vm.GetLocations(1)
|
|
if !found || len(locs) != 1 || locs[0].Url != regular.Url {
|
|
t.Fatalf("expected the freshly learned regular copy, got found=%v %v", found, urlsOf(locs))
|
|
}
|
|
})
|
|
|
|
t.Run("same generation prefers regular", func(t *testing.T) {
|
|
vm := newVidMap("", DefaultVidMapCacheSize)
|
|
vm.addEcLocation(1, ecShard)
|
|
vm.addLocation(1, regular)
|
|
|
|
locs, found := vm.GetLocations(1)
|
|
if !found || len(locs) != 1 || locs[0].Url != regular.Url {
|
|
t.Fatalf("expected the regular copy, got found=%v %v", found, urlsOf(locs))
|
|
}
|
|
})
|
|
}
|
|
|
|
// Losing the last location drops the entry: a client that never resets should
|
|
// not accumulate one empty entry per volume it has ever seen deleted.
|
|
func TestDeleteLastLocationDropsEntry(t *testing.T) {
|
|
vm := newVidMap("", DefaultVidMapCacheSize)
|
|
vid := uint32(12)
|
|
location := Location{Url: "10.0.0.1:8080"}
|
|
|
|
vm.addLocation(vid, location)
|
|
vm.deleteLocation(vid, location)
|
|
|
|
if _, found := vm.GetLocations(vid); found {
|
|
t.Error("a volume with no locations left must not resolve")
|
|
}
|
|
if len(vm.vid2Locations) != 0 {
|
|
t.Errorf("expected the emptied entry to be dropped, got %v", vm.vid2Locations)
|
|
}
|
|
if len(vm.serverRefCount) != 0 {
|
|
t.Errorf("server refcounts leaked: %v", vm.serverRefCount)
|
|
}
|
|
}
|
|
|
|
// EC locations follow the same rules as regular ones, and back a volume whose
|
|
// regular locations are gone.
|
|
func TestEcLocationsFollowGenerationRules(t *testing.T) {
|
|
vm := newVidMap("", DefaultVidMapCacheSize)
|
|
vid := uint32(8)
|
|
regular := Location{Url: "10.0.0.1:8080"}
|
|
ecShard := Location{Url: "10.0.0.2:8080"}
|
|
movedEcShard := Location{Url: "10.0.0.3:8080"}
|
|
|
|
vm.addLocation(vid, regular)
|
|
vm.addEcLocation(vid, ecShard)
|
|
|
|
locs, found := vm.GetLocations(vid)
|
|
if !found || len(locs) != 1 || locs[0].Url != regular.Url {
|
|
t.Fatalf("regular locations should win while they exist, got %v", urlsOf(locs))
|
|
}
|
|
|
|
// Regular copy goes away: the EC shards still serve the volume.
|
|
vm.deleteLocation(vid, regular)
|
|
locs, found = vm.GetLocations(vid)
|
|
if !found || len(locs) != 1 || locs[0].Url != ecShard.Url {
|
|
t.Fatalf("expected EC shard location, got found=%v %v", found, urlsOf(locs))
|
|
}
|
|
|
|
// EC shards move under a new master: no merging with the old report.
|
|
vm.reset()
|
|
vm.addEcLocation(vid, movedEcShard)
|
|
locs, found = vm.GetLocations(vid)
|
|
if !found || len(locs) != 1 || locs[0].Url != movedEcShard.Url {
|
|
t.Fatalf("expected only the moved EC shard, got found=%v %v", found, urlsOf(locs))
|
|
}
|
|
}
|
|
|
|
// Locations handed to a caller are never rewritten underneath it.
|
|
func TestGetLocationsResultIsStable(t *testing.T) {
|
|
vm := newVidMap("", DefaultVidMapCacheSize)
|
|
vid := uint32(9)
|
|
first := Location{Url: "10.0.0.1:8080"}
|
|
second := Location{Url: "10.0.0.2:8080"}
|
|
|
|
vm.addLocation(vid, first)
|
|
vm.addLocation(vid, second)
|
|
|
|
locs, found := vm.GetLocations(vid)
|
|
if !found || len(locs) != 2 {
|
|
t.Fatalf("expected both replicas, got %v", urlsOf(locs))
|
|
}
|
|
snapshot := append([]Location(nil), locs...)
|
|
|
|
vm.deleteLocation(vid, first)
|
|
vm.addLocation(vid, Location{Url: "10.0.0.3:8080"})
|
|
|
|
for i := range snapshot {
|
|
if locs[i] != snapshot[i] {
|
|
t.Errorf("location %d changed under the caller: %v became %v", i, snapshot[i], locs[i])
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestNewVidMapDefaultsRetention(t *testing.T) {
|
|
for _, retain := range []int{0, -1} {
|
|
if got := newVidMap("", retain).retainGenerations; got != DefaultVidMapCacheSize {
|
|
t.Errorf("newVidMap(%d) retention = %d, want %d", retain, got, DefaultVidMapCacheSize)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Readers must never see a spurious miss for a volume that stays live, and the
|
|
// bookkeeping must survive concurrent writers. Run with -race.
|
|
func TestConcurrentResetAndUpdates(t *testing.T) {
|
|
vm := newVidMap("", DefaultVidMapCacheSize)
|
|
live := Location{Url: "10.0.0.1:8080"}
|
|
churn := Location{Url: "10.0.0.2:8080"}
|
|
const liveVid, churnVid = 1, 2
|
|
|
|
vm.addLocation(liveVid, live)
|
|
|
|
var wg sync.WaitGroup
|
|
stop := make(chan struct{})
|
|
|
|
for i := 0; i < 8; i++ {
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
for {
|
|
select {
|
|
case <-stop:
|
|
return
|
|
default:
|
|
if _, found := vm.GetLocations(liveVid); !found {
|
|
t.Error("a volume that is relearned every generation must always resolve")
|
|
return
|
|
}
|
|
vm.hasVolumeServer(pb.ServerAddress(live.Url))
|
|
vm.GetLocationsClone(churnVid)
|
|
runtime.Gosched()
|
|
}
|
|
}
|
|
}()
|
|
}
|
|
|
|
for i := 0; i < 4; i++ {
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
for j := 0; j < 200; j++ {
|
|
vm.addEcLocation(churnVid, churn)
|
|
vm.deleteEcLocation(churnVid, churn)
|
|
vm.deleteVid(churnVid)
|
|
}
|
|
}()
|
|
}
|
|
|
|
for i := 0; i < 300; i++ {
|
|
vm.reset()
|
|
vm.addLocation(liveVid, live)
|
|
}
|
|
close(stop)
|
|
wg.Wait()
|
|
|
|
if _, found := vm.GetLocations(liveVid); !found {
|
|
t.Fatal("live volume lost after the churn")
|
|
}
|
|
vm.deleteVid(liveVid)
|
|
if len(vm.serverRefCount) != 0 {
|
|
t.Errorf("server refcounts leaked: %v", vm.serverRefCount)
|
|
}
|
|
}
|