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
297 lines
12 KiB
Go
297 lines
12 KiB
Go
package storage
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"github.com/seaweedfs/seaweedfs/weed/stats"
|
|
"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/util"
|
|
)
|
|
|
|
// dataShardCount is the data-shard count threaded into
|
|
// FindEcShardTargetLocation by these tests. Kept as a literal so the test
|
|
// stays valid when enterprise builds use a different default ratio.
|
|
const dataShardCount = 10
|
|
|
|
// TestFindEcShardTargetLocation_PinsToEcxOnDisk reproduces the placement
|
|
// half of issue #9212. ec.rebuild copies the .ecx alongside the first
|
|
// shard, then sends subsequent shards with CopyEcxFile=false relying on
|
|
// the volume server's auto-select to land them on the same disk. The
|
|
// volume isn't mounted yet, so FindEcVolume can't see the .ecx — without
|
|
// an on-disk check the selection falls back to "any HDD with free space"
|
|
// and shards end up split from their index files across disks of the
|
|
// same node.
|
|
//
|
|
// The fix: FindEcShardTargetLocation also looks for the .ecx on disk
|
|
// before falling through to the generic disk-space heuristic.
|
|
func TestFindEcShardTargetLocation_PinsToEcxOnDisk(t *testing.T) {
|
|
store := newEcTargetTestStore(t, 3)
|
|
collection := "grafana-loki"
|
|
vid := needle.VolumeId(1093)
|
|
|
|
// Drop a sealed .ecx onto disk 2. Nothing is mounted yet — this is
|
|
// the state right after ec.rebuild's first VolumeEcShardsCopy with
|
|
// CopyEcxFile=true and before any VolumeEcShardsMount has run.
|
|
base := erasure_coding.EcShardFileName(collection, store.Locations[2].IdxDirectory, int(vid))
|
|
if err := os.WriteFile(base+".ecx", make([]byte, 20), 0o644); err != nil {
|
|
t.Fatalf("seed .ecx on disk 2: %v", err)
|
|
}
|
|
|
|
got := store.FindEcShardTargetLocation(collection, vid, dataShardCount)
|
|
if got == nil {
|
|
t.Fatalf("FindEcShardTargetLocation returned nil; expected disk 2")
|
|
}
|
|
if got != store.Locations[2] {
|
|
t.Errorf("placement leaked off the .ecx-owning disk: got %s, want %s (issue #9212)",
|
|
got.Directory, store.Locations[2].Directory)
|
|
}
|
|
}
|
|
|
|
// TestFindEcShardTargetLocation_PrefersMountedOverEcx checks that an
|
|
// already-mounted EC volume on disk 1 wins over a stray .ecx on disk 2.
|
|
// This protects the post-startup steady state from being perturbed by
|
|
// leftover index files from a prior failed move.
|
|
func TestFindEcShardTargetLocation_PrefersMountedOverEcx(t *testing.T) {
|
|
store := newEcTargetTestStore(t, 3)
|
|
collection := "grafana-loki"
|
|
vid := needle.VolumeId(2222)
|
|
|
|
// Mount a placeholder EC volume on disk 1 so FindEcVolume returns it.
|
|
loc1 := store.Locations[1]
|
|
loc1.ecVolumesLock.Lock()
|
|
loc1.ecVolumes[vid] = &erasure_coding.EcVolume{VolumeId: vid, Collection: collection}
|
|
loc1.ecVolumesLock.Unlock()
|
|
|
|
// Drop a stray .ecx on disk 2 to make sure it does NOT win.
|
|
base := erasure_coding.EcShardFileName(collection, store.Locations[2].IdxDirectory, int(vid))
|
|
if err := os.WriteFile(base+".ecx", make([]byte, 20), 0o644); err != nil {
|
|
t.Fatalf("seed .ecx on disk 2: %v", err)
|
|
}
|
|
|
|
got := store.FindEcShardTargetLocation(collection, vid, dataShardCount)
|
|
if got != loc1 {
|
|
t.Errorf("placement should follow mounted EC volume on disk 1, got %v", got)
|
|
}
|
|
}
|
|
|
|
// TestFindEcShardTargetLocation_FallsThroughToHddWhenNothingMatches keeps
|
|
// the existing fallback behaviour intact for the cold-volume case (no
|
|
// mount, no .ecx anywhere on this server).
|
|
func TestFindEcShardTargetLocation_FallsThroughToHddWhenNothingMatches(t *testing.T) {
|
|
store := newEcTargetTestStore(t, 2)
|
|
collection := "grafana-loki"
|
|
vid := needle.VolumeId(3333)
|
|
|
|
got := store.FindEcShardTargetLocation(collection, vid, dataShardCount)
|
|
if got == nil {
|
|
t.Fatalf("FindEcShardTargetLocation returned nil; expected an HDD fallback")
|
|
}
|
|
if got.DiskType != types.HardDriveType {
|
|
t.Errorf("fallback should pick an HDD; got disk type %q", got.DiskType)
|
|
}
|
|
}
|
|
|
|
// TestFindEcShardTargetLocation_HonoursUnlimitedDisk pins the
|
|
// MaxVolumeCount==0 ("unlimited") convention shared with
|
|
// hasFreeDiskLocation. ecFreeShardCount used to return a negative free
|
|
// count for unlimited disks, which made FindEcShardTargetLocation skip
|
|
// them entirely. PR #9245 review by @gemini-code-assist.
|
|
func TestFindEcShardTargetLocation_HonoursUnlimitedDisk(t *testing.T) {
|
|
store := newEcTargetTestStore(t, 1)
|
|
store.Locations[0].MaxVolumeCount = 0 // unlimited
|
|
|
|
got := store.FindEcShardTargetLocation("grafana-loki", needle.VolumeId(4444), dataShardCount)
|
|
if got == nil {
|
|
t.Fatalf("FindEcShardTargetLocation returned nil for an unlimited (MaxVolumeCount=0) disk")
|
|
}
|
|
if got != store.Locations[0] {
|
|
t.Errorf("expected the only (unlimited) disk to be picked; got %v", got)
|
|
}
|
|
}
|
|
|
|
// TestFindEcShardTargetLocation_TightProvisioningKeepsEcxDisk pins the
|
|
// truncation hazard PR #9245 review by @coderabbitai surfaced.
|
|
//
|
|
// With MaxVolumeCount=1, VolumesLen=0, and one EC shard already on the
|
|
// disk, the previous formula (free = (1*10 - 1) / 10 = 0) would treat
|
|
// the disk as full and route subsequent shards to a different disk —
|
|
// exactly the orphan-shard layout this PR exists to prevent. The fix
|
|
// keeps the free count in shard slots, so 9 free slots is reported as
|
|
// 9 rather than rounded down to 0.
|
|
func TestFindEcShardTargetLocation_TightProvisioningKeepsEcxDisk(t *testing.T) {
|
|
store := newEcTargetTestStore(t, 2)
|
|
store.Locations[0].MaxVolumeCount = 1
|
|
store.Locations[1].MaxVolumeCount = 1
|
|
|
|
collection := "grafana-loki"
|
|
vid := needle.VolumeId(5555)
|
|
|
|
// Seed disk 1 with a single EC shard for this volume so it owns the
|
|
// .ecx and has 9 free shard slots remaining; the old formula would
|
|
// have rounded that to 0.
|
|
loc1 := store.Locations[1]
|
|
loc1.ecVolumesLock.Lock()
|
|
loc1.ecVolumes[vid] = &erasure_coding.EcVolume{
|
|
VolumeId: vid,
|
|
Collection: collection,
|
|
Shards: []*erasure_coding.EcVolumeShard{{VolumeId: vid, ShardId: 0, Collection: collection}},
|
|
}
|
|
loc1.ecVolumesLock.Unlock()
|
|
|
|
got := store.FindEcShardTargetLocation(collection, vid, dataShardCount)
|
|
if got != loc1 {
|
|
t.Errorf("expected the .ecx-owning disk (1 shard placed, 9 free shard slots) to be picked; got %v", got)
|
|
}
|
|
}
|
|
|
|
// TestFindEcShardTargetLocation_PinsToTheDiskOwningTheShard covers the
|
|
// per-server invariant that a shard id is owned by at most one disk.
|
|
//
|
|
// A multi-disk server legitimately mounts one vid on several disks, each
|
|
// holding a disjoint subset of the shards, so the mounted tier ties and the
|
|
// free-count tie-break decides — and it points at whichever disk happens to
|
|
// be emptier, not at the disk that already has this shard. A re-copy of a
|
|
// shard the server already holds (a retried ec.balance / ec.rebuild move)
|
|
// then lands a second copy on the sibling disk, and both disks register the
|
|
// same shard id.
|
|
func TestFindEcShardTargetLocation_PinsToTheDiskOwningTheShard(t *testing.T) {
|
|
store := newEcTargetTestStore(t, 2)
|
|
collection := "grafana-loki"
|
|
vid := needle.VolumeId(8888)
|
|
|
|
// Disk 0 owns shards 0 and 1, disk 1 owns shard 2 — so disk 1 is the
|
|
// emptier of the two and wins the free-count tie-break.
|
|
mountEcShards(store.Locations[0], collection, vid, 0, 1)
|
|
mountEcShards(store.Locations[1], collection, vid, 2)
|
|
|
|
if got := store.FindEcShardTargetLocation(collection, vid, dataShardCount, 0); got != store.Locations[0] {
|
|
t.Errorf("a copy of shard 0 left its owning disk: got %v, want %s", got, store.Locations[0].Directory)
|
|
}
|
|
if got := store.FindEcShardTargetLocation(collection, vid, dataShardCount, 2); got != store.Locations[1] {
|
|
t.Errorf("a copy of shard 2 left its owning disk: got %v, want %s", got, store.Locations[1].Directory)
|
|
}
|
|
|
|
// A shard no disk owns yet is placed by the unchanged waterfall: both
|
|
// disks have it mounted, so the emptier one wins.
|
|
if got := store.FindEcShardTargetLocation(collection, vid, dataShardCount, 7); got != store.Locations[1] {
|
|
t.Errorf("placement of an unclaimed shard changed: got %v, want the emptier mounted disk", got)
|
|
}
|
|
}
|
|
|
|
// TestFindEcShardTargetLocation_OwningDiskWinsWhenFull pins the second half
|
|
// of the invariant: a disk with no free shard slots still wins for a shard it
|
|
// already owns. Re-copying that shard overwrites bytes the disk is already
|
|
// accounted for, while the alternative — routing to a sibling — splits the
|
|
// claim across two disks.
|
|
func TestFindEcShardTargetLocation_OwningDiskWinsWhenFull(t *testing.T) {
|
|
store := newEcTargetTestStore(t, 2)
|
|
collection := "grafana-loki"
|
|
vid := needle.VolumeId(9999)
|
|
|
|
store.Locations[0].MaxVolumeCount = 1
|
|
mountEcShards(store.Locations[0], collection, vid, 0)
|
|
// Fill disk 0 past its shard-slot budget so ecFreeShardCount reports 0.
|
|
filler := needle.VolumeId(10000)
|
|
fillerShards := make([]erasure_coding.ShardId, dataShardCount)
|
|
for i := range fillerShards {
|
|
fillerShards[i] = erasure_coding.ShardId(i)
|
|
}
|
|
mountEcShards(store.Locations[0], collection, filler, fillerShards...)
|
|
|
|
if got := store.FindEcShardTargetLocation(collection, vid, dataShardCount, 0); got != store.Locations[0] {
|
|
t.Errorf("a copy of shard 0 left its owning disk when the disk was full: got %v", got)
|
|
}
|
|
// A shard it does not own still respects the space filter.
|
|
if got := store.FindEcShardTargetLocation(collection, vid, dataShardCount, 7); got != store.Locations[1] {
|
|
t.Errorf("an unclaimed shard should go to the disk with free slots: got %v", got)
|
|
}
|
|
}
|
|
|
|
// TestEcShardOwnerDisks pins the mixed-owner batch contract: a batch whose
|
|
// requested shards are already owned by different disks reports every owner,
|
|
// so VolumeEcShardsCopy can refuse it rather than rank the owners into one
|
|
// destination and duplicate the loser's claim.
|
|
func TestEcShardOwnerDisks(t *testing.T) {
|
|
store := newEcTargetTestStore(t, 3)
|
|
collection := "grafana-loki"
|
|
vid := needle.VolumeId(11111)
|
|
|
|
mountEcShards(store.Locations[0], collection, vid, 0, 1)
|
|
mountEcShards(store.Locations[1], collection, vid, 2)
|
|
|
|
if owners := store.EcShardOwnerDisks(vid, []erasure_coding.ShardId{0, 2}); len(owners) != 2 {
|
|
t.Errorf("a batch owned by two disks reported %d owners; the copy handler would duplicate a claim", len(owners))
|
|
}
|
|
// A batch on one disk, with or without unowned extras, has one owner.
|
|
if owners := store.EcShardOwnerDisks(vid, []erasure_coding.ShardId{0, 1, 7}); len(owners) != 1 || owners[0] != store.Locations[0] {
|
|
t.Errorf("a single-owner batch reported owners %v; want just disk 0", owners)
|
|
}
|
|
// A wholly unowned batch reports none — fresh placement stays allowed.
|
|
if owners := store.EcShardOwnerDisks(vid, []erasure_coding.ShardId{7, 8}); len(owners) != 0 {
|
|
t.Errorf("an unowned batch reported owners %v; want none", owners)
|
|
}
|
|
}
|
|
|
|
// mountEcShards registers an EcVolume for vid on loc claiming shardIds.
|
|
func mountEcShards(loc *DiskLocation, collection string, vid needle.VolumeId, shardIds ...erasure_coding.ShardId) {
|
|
ecVolume := &erasure_coding.EcVolume{VolumeId: vid, Collection: collection}
|
|
for _, shardId := range shardIds {
|
|
ecVolume.Shards = append(ecVolume.Shards, &erasure_coding.EcVolumeShard{
|
|
VolumeId: vid, ShardId: shardId, Collection: collection,
|
|
})
|
|
}
|
|
loc.ecVolumesLock.Lock()
|
|
loc.ecVolumes[vid] = ecVolume
|
|
loc.ecVolumesLock.Unlock()
|
|
}
|
|
|
|
// newEcTargetTestStore is a leaner cousin of the helper in
|
|
// store_load_balancing_test.go: it spins up an in-memory Store with N
|
|
// HDD disk locations under a single t.TempDir and consumes any heartbeat
|
|
// channel traffic so the placement helpers can be exercised directly.
|
|
func newEcTargetTestStore(t *testing.T, numDirs int) *Store {
|
|
t.Helper()
|
|
tempDir := t.TempDir()
|
|
dirs := make([]string, 0, numDirs)
|
|
maxCounts := make([]int32, 0, numDirs)
|
|
minFreeSpaces := make([]util.MinFreeSpace, 0, numDirs)
|
|
diskTypes := make([]types.DiskType, 0, numDirs)
|
|
for i := 0; i < numDirs; i++ {
|
|
dir := filepath.Join(tempDir, "data", filepath.Base(t.Name())+"-"+string(rune('a'+i)))
|
|
if err := os.MkdirAll(dir, 0o755); err != nil {
|
|
t.Fatalf("mkdir %s: %v", dir, err)
|
|
}
|
|
dirs = append(dirs, dir)
|
|
maxCounts = append(maxCounts, 100)
|
|
minFreeSpaces = append(minFreeSpaces, util.MinFreeSpace{})
|
|
diskTypes = append(diskTypes, types.HardDriveType)
|
|
}
|
|
diskIOProbeConfig := stats.DefaultDiskIOProbeConfig()
|
|
store := NewStore(nil, "localhost", 8080, 18080, "http://localhost:8080", "store-id",
|
|
dirs, maxCounts, minFreeSpaces, "", NeedleMapInMemory, diskTypes, nil, 3, diskIOProbeConfig,
|
|
)
|
|
done := make(chan struct{})
|
|
go func() {
|
|
for {
|
|
select {
|
|
case <-store.NewVolumesChan:
|
|
case <-store.NewEcShardsChan:
|
|
case <-store.DeletedVolumesChan:
|
|
case <-store.DeletedEcShardsChan:
|
|
case <-store.StateUpdateChan:
|
|
case <-done:
|
|
return
|
|
}
|
|
}
|
|
}()
|
|
t.Cleanup(func() {
|
|
store.Close()
|
|
close(done)
|
|
})
|
|
return store
|
|
}
|