snapshot of https://github.com/seaweedfs/seaweedfs.git (master, 2026-09-16; blobs > 131072B omitted)
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
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
This commit is contained in:
452
weed/storage/remote_tier_integration_test.go
Normal file
452
weed/storage/remote_tier_integration_test.go
Normal file
@@ -0,0 +1,452 @@
|
||||
package storage
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/seaweedfs/seaweedfs/weed/pb/volume_server_pb"
|
||||
"github.com/seaweedfs/seaweedfs/weed/storage/backend"
|
||||
"github.com/seaweedfs/seaweedfs/weed/storage/erasure_coding"
|
||||
"github.com/seaweedfs/seaweedfs/weed/storage/needle"
|
||||
"github.com/seaweedfs/seaweedfs/weed/storage/super_block"
|
||||
"github.com/seaweedfs/seaweedfs/weed/storage/types"
|
||||
"github.com/seaweedfs/seaweedfs/weed/util"
|
||||
)
|
||||
|
||||
// In-process integration tests for cloud-tiered ("remote") volumes.
|
||||
//
|
||||
// Cover the operations the user is likely to schedule against a tiered
|
||||
// volume — balance/move, vacuum, EC encode, EC decode — exercising the real
|
||||
// Volume / DiskLocation / Store code paths against a fake BackendStorage that
|
||||
// stores objects in a temp dir. The fake stands in for S3/rclone/etc. so the
|
||||
// tests stay hermetic and fast.
|
||||
|
||||
// localDirBackend is a BackendStorage that stores objects as files in a
|
||||
// temp directory. It deletes from / writes to the dir under a mutex so the
|
||||
// tests can observe ordering (e.g. that a remote object survives a move).
|
||||
type localDirBackend struct {
|
||||
root string
|
||||
|
||||
mu sync.Mutex
|
||||
deletes []string // history of DeleteFile keys, for assertions
|
||||
}
|
||||
|
||||
func newLocalDirBackend(t *testing.T) *localDirBackend {
|
||||
t.Helper()
|
||||
root := t.TempDir()
|
||||
return &localDirBackend{root: root}
|
||||
}
|
||||
|
||||
func (b *localDirBackend) ToProperties() map[string]string {
|
||||
return map[string]string{"root": b.root}
|
||||
}
|
||||
|
||||
func (b *localDirBackend) NewStorageFile(key string, tierInfo *volume_server_pb.VolumeInfo) backend.BackendStorageFile {
|
||||
return &localDirBackendFile{backend: b, key: key, tierInfo: tierInfo}
|
||||
}
|
||||
|
||||
func (b *localDirBackend) CopyFile(f *os.File, fn func(progressed int64, percentage float32) error, concurrency int) (key string, size int64, err error) {
|
||||
key = fmt.Sprintf("obj-%d-%d", time.Now().UnixNano(), os.Getpid())
|
||||
dst := filepath.Join(b.root, key)
|
||||
out, err := os.Create(dst)
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
}
|
||||
defer out.Close()
|
||||
if _, err = f.Seek(0, io.SeekStart); err != nil {
|
||||
return "", 0, err
|
||||
}
|
||||
written, err := io.Copy(out, f)
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
}
|
||||
if fn != nil {
|
||||
_ = fn(written, 100)
|
||||
}
|
||||
return key, written, nil
|
||||
}
|
||||
|
||||
func (b *localDirBackend) DownloadFile(fileName string, key string, fn func(progressed int64, percentage float32) error, concurrency int) (size int64, err error) {
|
||||
src := filepath.Join(b.root, key)
|
||||
in, err := os.Open(src)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer in.Close()
|
||||
out, err := os.Create(fileName)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer out.Close()
|
||||
written, err := io.Copy(out, in)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if fn != nil {
|
||||
_ = fn(written, 100)
|
||||
}
|
||||
return written, nil
|
||||
}
|
||||
|
||||
func (b *localDirBackend) DeleteFile(key string) error {
|
||||
b.mu.Lock()
|
||||
b.deletes = append(b.deletes, key)
|
||||
b.mu.Unlock()
|
||||
return os.Remove(filepath.Join(b.root, key))
|
||||
}
|
||||
|
||||
func (b *localDirBackend) deleteHistory() []string {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
out := make([]string, len(b.deletes))
|
||||
copy(out, b.deletes)
|
||||
return out
|
||||
}
|
||||
|
||||
func (b *localDirBackend) objectExists(key string) bool {
|
||||
_, err := os.Stat(filepath.Join(b.root, key))
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// localDirBackendFile satisfies BackendStorageFile by reading/writing through
|
||||
// the file in the temp dir keyed by the .vif's stored object key. Size and
|
||||
// modtime come from the cached tierInfo, mirroring the S3 backend's
|
||||
// behavior of returning .vif metadata from GetStat.
|
||||
type localDirBackendFile struct {
|
||||
backend *localDirBackend
|
||||
key string
|
||||
tierInfo *volume_server_pb.VolumeInfo
|
||||
}
|
||||
|
||||
func (f *localDirBackendFile) ReadAt(p []byte, off int64) (int, error) {
|
||||
in, err := os.Open(filepath.Join(f.backend.root, f.key))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer in.Close()
|
||||
return in.ReadAt(p, off)
|
||||
}
|
||||
|
||||
func (f *localDirBackendFile) WriteAt(p []byte, off int64) (int, error) {
|
||||
out, err := os.OpenFile(filepath.Join(f.backend.root, f.key), os.O_RDWR|os.O_CREATE, 0o644)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer out.Close()
|
||||
return out.WriteAt(p, off)
|
||||
}
|
||||
|
||||
func (f *localDirBackendFile) Truncate(off int64) error {
|
||||
return os.Truncate(filepath.Join(f.backend.root, f.key), off)
|
||||
}
|
||||
|
||||
func (f *localDirBackendFile) Close() error { return nil }
|
||||
func (f *localDirBackendFile) Name() string { return f.key }
|
||||
func (f *localDirBackendFile) Sync() error { return nil }
|
||||
func (f *localDirBackendFile) GetStat() (int64, time.Time, error) {
|
||||
files := f.tierInfo.GetFiles()
|
||||
if len(files) == 0 {
|
||||
return 0, time.Time{}, fmt.Errorf("remote file info not found")
|
||||
}
|
||||
return int64(files[0].FileSize), time.Unix(int64(files[0].ModifiedTime), 0), nil
|
||||
}
|
||||
|
||||
const (
|
||||
testBackendName = "test_local_dir.default"
|
||||
)
|
||||
|
||||
// registerTestBackend installs the fake backend in the global registry under
|
||||
// testBackendName for the duration of one test. Volume.Destroy and tier
|
||||
// upload look this map up by name, so the registration must outlive the
|
||||
// volume operations exercised below.
|
||||
func registerTestBackend(t *testing.T, b *localDirBackend) {
|
||||
t.Helper()
|
||||
backend.BackendStorages[testBackendName] = b
|
||||
t.Cleanup(func() {
|
||||
delete(backend.BackendStorages, testBackendName)
|
||||
})
|
||||
}
|
||||
|
||||
// tierUpVolumeLive creates a real on-disk volume, writes a few needles, then
|
||||
// uploads the .dat to the fake backend and rewrites the volume in remote mode
|
||||
// (mirrors the production flow in volume_grpc_tier_upload.go but in-process).
|
||||
// It returns the still-open volume, exactly as a volume server holds it after a
|
||||
// live `volume.tier.upload` — no reload. Callers that want the on-disk state a
|
||||
// server sees after restart use tierUpVolume, which closes it.
|
||||
func tierUpVolumeLive(t *testing.T, dir string, vid needle.VolumeId, b *localDirBackend) (v *Volume, key string) {
|
||||
t.Helper()
|
||||
v, err := NewVolume(dir, dir, "", vid, NeedleMapInMemory, &super_block.ReplicaPlacement{}, &needle.TTL{}, 0, needle.GetCurrentVersion(), 0, 0)
|
||||
require.NoError(t, err)
|
||||
|
||||
for i := 1; i <= 5; i++ {
|
||||
_, _, _, err := v.writeNeedle2(newRandomNeedle(uint64(i)), true, false, false)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
diskFile, ok := v.DataBackend.(*backend.DiskFile)
|
||||
require.True(t, ok, "expected on-disk backend before tier-up")
|
||||
|
||||
uploadKey, size, err := b.CopyFile(diskFile.File, nil, 0)
|
||||
require.NoError(t, err)
|
||||
|
||||
bType, bId := backend.BackendNameToTypeId(testBackendName)
|
||||
v.GetVolumeInfo().Files = append(v.GetVolumeInfo().GetFiles(), &volume_server_pb.RemoteFile{
|
||||
BackendType: bType,
|
||||
BackendId: bId,
|
||||
Key: uploadKey,
|
||||
Offset: 0,
|
||||
FileSize: uint64(size),
|
||||
ModifiedTime: uint64(time.Now().Unix()),
|
||||
Extension: ".dat",
|
||||
})
|
||||
require.NoError(t, v.SaveVolumeInfo())
|
||||
require.NoError(t, v.LoadRemoteFile())
|
||||
require.NoError(t, os.Remove(v.FileName(".dat")))
|
||||
|
||||
return v, uploadKey
|
||||
}
|
||||
|
||||
// tierUpVolume runs tierUpVolumeLive then closes the volume. Tests using it
|
||||
// reload from disk to mirror what a volume server does on restart with a
|
||||
// tiered volume.
|
||||
func tierUpVolume(t *testing.T, dir string, vid needle.VolumeId, b *localDirBackend) (collection string, key string) {
|
||||
t.Helper()
|
||||
v, uploadKey := tierUpVolumeLive(t, dir, vid, b)
|
||||
v.Close()
|
||||
return v.Collection, uploadKey
|
||||
}
|
||||
|
||||
// reloadVolume loads an existing volume from disk, the way a volume server
|
||||
// does at startup. Returns the live volume with its async write worker
|
||||
// running, so Destroy's channel close is valid.
|
||||
func reloadVolume(t *testing.T, dir string, vid needle.VolumeId) *Volume {
|
||||
t.Helper()
|
||||
v, err := NewVolume(dir, dir, "", vid, NeedleMapInMemory, &super_block.ReplicaPlacement{}, &needle.TTL{}, 0, needle.GetCurrentVersion(), 0, 0)
|
||||
require.NoError(t, err)
|
||||
return v
|
||||
}
|
||||
|
||||
// TestRemoteTier_DiskScanLoadsRemoteOnlyVolume locks in that the disk scan
|
||||
// (loadExistingVolume) loads a remote-only volume — a .vif pointing at remote
|
||||
// files with no local .dat — instead of skipping it as a lone sidecar. The
|
||||
// phantom-.dat guard must let remote volumes through.
|
||||
func TestRemoteTier_DiskScanLoadsRemoteOnlyVolume(t *testing.T) {
|
||||
b := newLocalDirBackend(t)
|
||||
registerTestBackend(t, b)
|
||||
|
||||
dir := t.TempDir()
|
||||
const vid = needle.VolumeId(44)
|
||||
tierUpVolume(t, dir, vid, b) // leaves .vif (remote) + .idx, no .dat
|
||||
|
||||
require.False(t, util.FileExists(filepath.Join(dir, "44.dat")), "tier-up should have removed the local .dat")
|
||||
|
||||
loc := &DiskLocation{
|
||||
Directory: dir,
|
||||
DirectoryUuid: "test-uuid",
|
||||
IdxDirectory: dir,
|
||||
DiskType: types.HddType,
|
||||
MaxVolumeCount: 100,
|
||||
OriginalMaxVolumeCount: 100,
|
||||
MinFreeSpace: util.MinFreeSpace{Type: util.AsPercent, Percent: 1, Raw: "1"},
|
||||
}
|
||||
loc.volumes = make(map[needle.VolumeId]*Volume)
|
||||
loc.ecVolumes = make(map[needle.VolumeId]*erasure_coding.EcVolume)
|
||||
|
||||
loc.loadExistingVolumes(NeedleMapInMemory, 0)
|
||||
|
||||
v, ok := loc.volumes[vid]
|
||||
require.True(t, ok, "remote-only volume must be loaded by the disk scan, not skipped by the phantom-.dat guard")
|
||||
require.True(t, v.HasRemoteFile(), "loaded volume should be in remote mode")
|
||||
v.Close()
|
||||
}
|
||||
|
||||
// TestRemoteTier_LiveTierUpload_StillReportsToMaster covers a live
|
||||
// `volume.tier.upload`: the .dat is removed and the volume serves from remote,
|
||||
// but the same in-memory Volume keeps heartbeating with no reload. The
|
||||
// phantom-.dat guard must not suppress it just because .dat is gone —
|
||||
// LoadRemoteFile has flipped it into remote mode, so ToVolumeInformationMessage
|
||||
// must still report it to the master. If HasRemoteFile stayed false the volume
|
||||
// would vanish from the topology ("volume not found").
|
||||
func TestRemoteTier_LiveTierUpload_StillReportsToMaster(t *testing.T) {
|
||||
b := newLocalDirBackend(t)
|
||||
registerTestBackend(t, b)
|
||||
|
||||
dir := t.TempDir()
|
||||
const vid = needle.VolumeId(67)
|
||||
v, _ := tierUpVolumeLive(t, dir, vid, b)
|
||||
defer v.Close()
|
||||
// A store-owned volume always carries its DiskLocation; NewVolume leaves it
|
||||
// nil, so give it one for the IsReadOnly disk-space check inside the heartbeat.
|
||||
v.location = &DiskLocation{Directory: dir, DiskType: types.HddType}
|
||||
|
||||
require.True(t, v.HasRemoteFile(), "a tier-uploaded volume is in remote mode even before any reload")
|
||||
require.False(t, util.FileExists(v.FileName(".dat")), "tier-up should have removed the local .dat")
|
||||
|
||||
_, msg := v.ToVolumeInformationMessage(nil)
|
||||
require.NotNil(t, msg, "tier-uploaded volume must still report to master")
|
||||
require.NotEmpty(t, msg.RemoteStorageName, "reported volume must carry its remote backend name")
|
||||
}
|
||||
|
||||
// TestRemoteTier_ReloadUnderDataLock_NoDeadlock guards the reload-under-lock
|
||||
// path: CommitCompact holds dataFileAccessLock and calls v.load(), which for a
|
||||
// remote-tiered volume swaps the data backend. That swap must go through the
|
||||
// lock-free loadRemoteFileLocked; if load() instead used the public
|
||||
// LoadRemoteFile (which takes dataFileAccessLock), it would re-enter the held
|
||||
// lock and deadlock.
|
||||
func TestRemoteTier_ReloadUnderDataLock_NoDeadlock(t *testing.T) {
|
||||
b := newLocalDirBackend(t)
|
||||
registerTestBackend(t, b)
|
||||
|
||||
dir := t.TempDir()
|
||||
const vid = needle.VolumeId(68)
|
||||
v, _ := tierUpVolumeLive(t, dir, vid, b)
|
||||
v.location = &DiskLocation{Directory: dir, DiskType: types.HddType}
|
||||
require.True(t, v.HasRemoteFile())
|
||||
|
||||
done := make(chan error, 1)
|
||||
go func() {
|
||||
// Mirror CommitCompact: hold the data lock across the reload.
|
||||
v.dataFileAccessLock.Lock()
|
||||
defer v.dataFileAccessLock.Unlock()
|
||||
done <- v.load(true, false, v.needleMapKind, 0, v.Version())
|
||||
}()
|
||||
|
||||
select {
|
||||
case err := <-done:
|
||||
require.NoError(t, err)
|
||||
require.True(t, v.HasRemoteFile(), "volume must stay remote-tiered after reload")
|
||||
v.Close()
|
||||
case <-time.After(10 * time.Second):
|
||||
t.Fatal("reload under dataFileAccessLock deadlocked: load() re-entered the held lock via LoadRemoteFile")
|
||||
}
|
||||
}
|
||||
|
||||
// TestRemoteTier_Move_KeepsRemoteObject simulates the move-on-source-after-copy
|
||||
// step of a balance: Destroy(onlyEmpty=false, keepRemoteData=true). The remote
|
||||
// object must survive — the destination's freshly-copied .vif points at it.
|
||||
func TestRemoteTier_Move_KeepsRemoteObject(t *testing.T) {
|
||||
b := newLocalDirBackend(t)
|
||||
registerTestBackend(t, b)
|
||||
|
||||
dir := t.TempDir()
|
||||
const vid = needle.VolumeId(31)
|
||||
_, key := tierUpVolume(t, dir, vid, b)
|
||||
require.True(t, b.objectExists(key), "remote object missing after tier-up")
|
||||
|
||||
v := reloadVolume(t, dir, vid)
|
||||
require.True(t, v.HasRemoteFile())
|
||||
|
||||
require.NoError(t, v.Destroy(false, true))
|
||||
|
||||
require.True(t, b.objectExists(key), "Destroy(keepRemoteData=true) must not delete remote object")
|
||||
require.Empty(t, b.deleteHistory(), "no DeleteFile call expected on a move-style destroy")
|
||||
}
|
||||
|
||||
// TestRemoteTier_RealDelete_RemovesRemoteObject is the inverse: a true delete
|
||||
// (keepRemoteData=false) must clean up the remote object. Locks in that we
|
||||
// did not accidentally turn the new flag into a global skip.
|
||||
func TestRemoteTier_RealDelete_RemovesRemoteObject(t *testing.T) {
|
||||
b := newLocalDirBackend(t)
|
||||
registerTestBackend(t, b)
|
||||
|
||||
dir := t.TempDir()
|
||||
const vid = needle.VolumeId(32)
|
||||
_, key := tierUpVolume(t, dir, vid, b)
|
||||
|
||||
v := reloadVolume(t, dir, vid)
|
||||
require.True(t, v.HasRemoteFile())
|
||||
|
||||
require.NoError(t, v.Destroy(false, false))
|
||||
|
||||
require.False(t, b.objectExists(key), "Destroy(keepRemoteData=false) must delete remote object")
|
||||
require.Equal(t, []string{key}, b.deleteHistory())
|
||||
}
|
||||
|
||||
// TestRemoteTier_Vacuum_DoesNotDeleteRemote runs the compact paths against a
|
||||
// remote-tier volume and asserts the safety property: regardless of whether
|
||||
// compact succeeds, errors, or no-ops, it must not delete the cloud object.
|
||||
func TestRemoteTier_Vacuum_DoesNotDeleteRemote(t *testing.T) {
|
||||
b := newLocalDirBackend(t)
|
||||
registerTestBackend(t, b)
|
||||
|
||||
dir := t.TempDir()
|
||||
const vid = needle.VolumeId(33)
|
||||
_, key := tierUpVolume(t, dir, vid, b)
|
||||
require.True(t, b.objectExists(key))
|
||||
|
||||
v := reloadVolume(t, dir, vid)
|
||||
defer v.Close()
|
||||
require.True(t, v.HasRemoteFile())
|
||||
|
||||
_ = v.CompactByVolumeData(nil)
|
||||
_ = v.CompactByIndex(nil)
|
||||
require.True(t, b.objectExists(key), "Compact must not delete remote object")
|
||||
require.Empty(t, b.deleteHistory())
|
||||
}
|
||||
|
||||
// TestRemoteTier_ECEncode_RequiresLocalDat confirms the EC encoder runs
|
||||
// against the local .dat path. For a remote-tier volume the .dat is gone,
|
||||
// so encoding is expected to fail with a missing-file error — locks in that
|
||||
// callers must download (tier_move_dat_from_remote) before encoding.
|
||||
func TestRemoteTier_ECEncode_RequiresLocalDat(t *testing.T) {
|
||||
b := newLocalDirBackend(t)
|
||||
registerTestBackend(t, b)
|
||||
|
||||
dir := t.TempDir()
|
||||
const vid = needle.VolumeId(34)
|
||||
tierUpVolume(t, dir, vid, b)
|
||||
|
||||
baseFileName := filepath.Join(dir, fmt.Sprintf("%d", uint32(vid)))
|
||||
_, err := erasure_coding.WriteEcFiles(baseFileName, erasure_coding.BackgroundECContext())
|
||||
require.Error(t, err, "EC encoder must not run with .dat missing — caller is expected to download first")
|
||||
require.Contains(t, err.Error(), ".dat")
|
||||
}
|
||||
|
||||
// TestRemoteTier_ECEncodeDecode_AfterDownload exercises the encode/decode
|
||||
// round-trip on a tiered volume after pulling the .dat back to local disk
|
||||
// (the production sequence used by `volume.tier.move -dest=local`).
|
||||
func TestRemoteTier_ECEncodeDecode_AfterDownload(t *testing.T) {
|
||||
b := newLocalDirBackend(t)
|
||||
registerTestBackend(t, b)
|
||||
|
||||
dir := t.TempDir()
|
||||
const vid = needle.VolumeId(35)
|
||||
_, key := tierUpVolume(t, dir, vid, b)
|
||||
|
||||
baseFileName := filepath.Join(dir, fmt.Sprintf("%d", uint32(vid)))
|
||||
datPath := baseFileName + ".dat"
|
||||
_, err := b.DownloadFile(datPath, key, nil, 0)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.NoError(t, erasure_coding.WriteSortedFileFromIdx(baseFileName, ".ecx"))
|
||||
_, ecErr := erasure_coding.WriteEcFiles(baseFileName, erasure_coding.BackgroundECContext())
|
||||
require.NoError(t, ecErr)
|
||||
|
||||
for i := 0; i < erasure_coding.TotalShardsCount; i++ {
|
||||
shardPath := fmt.Sprintf("%s.ec%02d", baseFileName, i)
|
||||
_, statErr := os.Stat(shardPath)
|
||||
require.NoError(t, statErr, "shard %d missing after encode", i)
|
||||
}
|
||||
|
||||
// Drop the parity-range shards (indices DataShardsCount..TotalShardsCount-1)
|
||||
// and rebuild — exercises the recover-from-missing-parity path.
|
||||
for i := erasure_coding.DataShardsCount; i < erasure_coding.TotalShardsCount; i++ {
|
||||
shardPath := fmt.Sprintf("%s.ec%02d", baseFileName, i)
|
||||
require.NoError(t, os.Remove(shardPath))
|
||||
}
|
||||
rebuilt, err := erasure_coding.RebuildEcFiles(baseFileName, erasure_coding.BackgroundECContext(), false)
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, rebuilt, "rebuild should report which parity shards were regenerated")
|
||||
for i := erasure_coding.DataShardsCount; i < erasure_coding.TotalShardsCount; i++ {
|
||||
shardPath := fmt.Sprintf("%s.ec%02d", baseFileName, i)
|
||||
_, statErr := os.Stat(shardPath)
|
||||
require.NoError(t, statErr, "parity shard %d missing after rebuild", i)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user