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:
531
weed/storage/volume.go
Normal file
531
weed/storage/volume.go
Normal file
@@ -0,0 +1,531 @@
|
||||
package storage
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/seaweedfs/seaweedfs/weed/pb/master_pb"
|
||||
"github.com/seaweedfs/seaweedfs/weed/pb/volume_server_pb"
|
||||
"github.com/seaweedfs/seaweedfs/weed/stats"
|
||||
"github.com/seaweedfs/seaweedfs/weed/storage/backend"
|
||||
"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/storage/volume_info"
|
||||
|
||||
"github.com/seaweedfs/seaweedfs/weed/glog"
|
||||
)
|
||||
|
||||
type Volume struct {
|
||||
Id needle.VolumeId
|
||||
dir string
|
||||
dirIdx string
|
||||
Collection string
|
||||
DataBackend backend.BackendStorageFile
|
||||
nm NeedleMapper
|
||||
tmpNm TempNeedleMapper
|
||||
needleMapKind NeedleMapKind
|
||||
noWriteOrDelete bool // if readonly, either noWriteOrDelete or noWriteCanDelete
|
||||
noWriteCanDelete bool // if readonly, either noWriteOrDelete or noWriteCanDelete
|
||||
noWriteLock sync.RWMutex
|
||||
hasRemoteFile atomic.Bool // if the volume is tiered: data lives in a remote backend
|
||||
MemoryMapMaxSizeMb uint32
|
||||
|
||||
super_block.SuperBlock
|
||||
|
||||
dataFileAccessLock sync.RWMutex
|
||||
superBlockAccessLock sync.Mutex
|
||||
|
||||
// The batch worker exists only once the volume takes a durable write. Most
|
||||
// never do -- read-only, remote-tiered, or written without fsync -- and a
|
||||
// parked worker costs its goroutine stack plus a 128-slot channel, which a
|
||||
// server holding millions of volumes cannot pay for all of them.
|
||||
asyncWorkerLock sync.Mutex
|
||||
asyncRequestsChan chan *needle.AsyncRequest
|
||||
asyncWorkerClosed bool
|
||||
|
||||
lastModifiedTsSeconds uint64 // unix time in seconds
|
||||
lastAppendAtNs uint64 // unix time in nanoseconds
|
||||
|
||||
lastCompactIndexOffset uint64
|
||||
lastCompactRevision uint16
|
||||
ldbTimeout int64
|
||||
|
||||
isCompactionInProgress atomic.Bool
|
||||
lastDiskCheckNs atomic.Int64 // unix time in nanoseconds for phantom volume detection
|
||||
|
||||
volumeInfoRWLock sync.RWMutex
|
||||
volumeInfo *volume_server_pb.VolumeInfo
|
||||
location *DiskLocation
|
||||
diskId uint32 // ID of this volume's disk in Store.Locations array
|
||||
|
||||
IoErrorTracker
|
||||
}
|
||||
|
||||
func NewVolume(dirname string, dirIdx string, collection string, id needle.VolumeId, needleMapKind NeedleMapKind, replicaPlacement *super_block.ReplicaPlacement, ttl *needle.TTL, preallocate int64, ver needle.Version, memoryMapMaxSizeMb uint32, ldbTimeout int64) (v *Volume, e error) {
|
||||
// if replicaPlacement is nil, the superblock will be loaded from disk
|
||||
v = &Volume{dir: dirname, dirIdx: dirIdx, Collection: collection, Id: id, MemoryMapMaxSizeMb: memoryMapMaxSizeMb}
|
||||
v.SuperBlock = super_block.SuperBlock{ReplicaPlacement: replicaPlacement, Ttl: ttl}
|
||||
v.needleMapKind = needleMapKind
|
||||
v.ldbTimeout = ldbTimeout
|
||||
e = v.load(true, true, needleMapKind, preallocate, ver)
|
||||
return
|
||||
}
|
||||
|
||||
func (v *Volume) String() string {
|
||||
v.noWriteLock.RLock()
|
||||
defer v.noWriteLock.RUnlock()
|
||||
return fmt.Sprintf("Id:%v dir:%s dirIdx:%s Collection:%s dataFile:%v nm:%v noWrite:%v canDelete:%v", v.Id, v.dir, v.dirIdx, v.Collection, v.DataBackend, v.nm, v.noWriteOrDelete || v.noWriteCanDelete, v.noWriteCanDelete)
|
||||
}
|
||||
|
||||
func VolumeFileName(dir string, collection string, id int) (fileName string) {
|
||||
idString := strconv.Itoa(id)
|
||||
if collection == "" {
|
||||
fileName = path.Join(dir, idString)
|
||||
} else {
|
||||
fileName = path.Join(dir, collection+"_"+idString)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (v *Volume) DataFileName() (fileName string) {
|
||||
return VolumeFileName(v.dir, v.Collection, int(v.Id))
|
||||
}
|
||||
|
||||
func (v *Volume) IndexFileName() (fileName string) {
|
||||
return VolumeFileName(v.dirIdx, v.Collection, int(v.Id))
|
||||
}
|
||||
|
||||
func (v *Volume) FileName(ext string) (fileName string) {
|
||||
switch ext {
|
||||
case ".idx", ".cpx", ".ldb", ".cpldb", ".rdb":
|
||||
return VolumeFileName(v.dirIdx, v.Collection, int(v.Id)) + ext
|
||||
}
|
||||
// .dat, .cpd, .vif
|
||||
return VolumeFileName(v.dir, v.Collection, int(v.Id)) + ext
|
||||
}
|
||||
|
||||
// RelocateIndexTo moves the volume's index to newIdxDir and reopens the volume
|
||||
// against it in place, without unmounting. It takes the data-file write lock —
|
||||
// so a concurrent read blocks briefly instead of failing — closes the needle
|
||||
// map and data backend, moves the .idx (and the derived .sdx best-effort), then
|
||||
// retargets dirIdx and reloads, mirroring CommitCompact's close-swap-load. A
|
||||
// decode co-locates the rebuilt index with the data so the on-demand mount can
|
||||
// find the volume; this returns it to the -dir.idx tier once the EC shards are
|
||||
// gone. A no-op when the index already lives in newIdxDir. A derived .ldb is
|
||||
// not moved: the reload rebuilds it in newIdxDir from the .idx.
|
||||
func (v *Volume) RelocateIndexTo(newIdxDir string) error {
|
||||
v.dataFileAccessLock.Lock()
|
||||
defer v.dataFileAccessLock.Unlock()
|
||||
|
||||
if v.dirIdx == newIdxDir {
|
||||
return nil
|
||||
}
|
||||
oldBase := VolumeFileName(v.dirIdx, v.Collection, int(v.Id))
|
||||
if _, err := os.Stat(oldBase + ".idx"); err != nil {
|
||||
return nil // nothing co-located to move
|
||||
}
|
||||
newBase := VolumeFileName(newIdxDir, v.Collection, int(v.Id))
|
||||
|
||||
if v.nm != nil {
|
||||
_ = v.nm.Sync()
|
||||
v.nm.Close()
|
||||
v.nm = nil
|
||||
}
|
||||
if v.DataBackend != nil {
|
||||
_ = v.DataBackend.Sync()
|
||||
_ = v.DataBackend.Close()
|
||||
v.DataBackend = nil
|
||||
}
|
||||
|
||||
if err := RenameOrCopyFile(oldBase+".idx", newBase+".idx"); err != nil {
|
||||
// Reopen against the old dir so the volume is not left down; surface a
|
||||
// failed reopen since it leaves the volume unusable until the next load.
|
||||
if reopenErr := v.load(true, false, v.needleMapKind, 0, v.Version()); reopenErr != nil {
|
||||
glog.Errorf("relocate volume %d: reopen after failed .idx move: %v", v.Id, reopenErr)
|
||||
}
|
||||
return fmt.Errorf("relocate index for volume %d: move .idx: %w", v.Id, err)
|
||||
}
|
||||
// The .sdx is a derived sorted index; move it when present, but a failure is
|
||||
// not fatal — drop the stale copy so the reload rebuilds it in the new dir.
|
||||
if _, err := os.Stat(oldBase + ".sdx"); err == nil {
|
||||
if err := RenameOrCopyFile(oldBase+".sdx", newBase+".sdx"); err != nil {
|
||||
glog.Warningf("relocate volume %d: move .sdx: %v (will rebuild)", v.Id, err)
|
||||
_ = os.Remove(oldBase + ".sdx")
|
||||
}
|
||||
}
|
||||
v.dirIdx = newIdxDir
|
||||
return v.load(true, false, v.needleMapKind, 0, v.Version())
|
||||
}
|
||||
|
||||
func (v *Volume) Version() needle.Version {
|
||||
v.superBlockAccessLock.Lock()
|
||||
defer v.superBlockAccessLock.Unlock()
|
||||
if v.volumeInfo.Version != 0 {
|
||||
v.SuperBlock.Version = needle.Version(v.volumeInfo.Version)
|
||||
}
|
||||
return v.SuperBlock.Version
|
||||
}
|
||||
|
||||
func (v *Volume) FileStat() (datSize uint64, idxSize uint64, modTime time.Time) {
|
||||
v.dataFileAccessLock.RLock()
|
||||
defer v.dataFileAccessLock.RUnlock()
|
||||
|
||||
if v.DataBackend == nil {
|
||||
return
|
||||
}
|
||||
|
||||
datFileSize, modTime, e := v.DataBackend.GetStat()
|
||||
if e == nil {
|
||||
return uint64(datFileSize), v.nm.IndexFileSize(), modTime
|
||||
}
|
||||
glog.V(0).Infof("Failed to read file size %s %v", v.DataBackend.Name(), e)
|
||||
return // -1 causes integer overflow and the volume to become unwritable.
|
||||
}
|
||||
|
||||
func (v *Volume) ContentSize() uint64 {
|
||||
v.dataFileAccessLock.RLock()
|
||||
defer v.dataFileAccessLock.RUnlock()
|
||||
if v.nm == nil {
|
||||
return 0
|
||||
}
|
||||
return v.nm.ContentSize()
|
||||
}
|
||||
|
||||
func (v *Volume) doIsEmpty() (bool, error) {
|
||||
// check v.DataBackend.GetStat()
|
||||
if v.DataBackend == nil {
|
||||
return false, fmt.Errorf("v.DataBackend is nil")
|
||||
} else {
|
||||
datFileSize, _, e := v.DataBackend.GetStat()
|
||||
if e != nil {
|
||||
glog.V(0).Infof("Failed to read file size %s %v", v.DataBackend.Name(), e)
|
||||
return false, fmt.Errorf("v.DataBackend.GetStat(): %v", e)
|
||||
}
|
||||
if datFileSize > super_block.SuperBlockSize {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
// check v.nm.ContentSize()
|
||||
if v.nm != nil {
|
||||
if v.nm.ContentSize() > 0 {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (v *Volume) DeletedSize() uint64 {
|
||||
v.dataFileAccessLock.RLock()
|
||||
defer v.dataFileAccessLock.RUnlock()
|
||||
if v.nm == nil {
|
||||
return 0
|
||||
}
|
||||
return v.nm.DeletedSize()
|
||||
}
|
||||
|
||||
func (v *Volume) FileCount() uint64 {
|
||||
v.dataFileAccessLock.RLock()
|
||||
defer v.dataFileAccessLock.RUnlock()
|
||||
if v.nm == nil {
|
||||
return 0
|
||||
}
|
||||
return uint64(v.nm.FileCount())
|
||||
}
|
||||
|
||||
func (v *Volume) DeletedCount() uint64 {
|
||||
v.dataFileAccessLock.RLock()
|
||||
defer v.dataFileAccessLock.RUnlock()
|
||||
if v.nm == nil {
|
||||
return 0
|
||||
}
|
||||
return uint64(v.nm.DeletedCount())
|
||||
}
|
||||
|
||||
func (v *Volume) MaxFileKey() types.NeedleId {
|
||||
v.dataFileAccessLock.RLock()
|
||||
defer v.dataFileAccessLock.RUnlock()
|
||||
if v.nm == nil {
|
||||
return 0
|
||||
}
|
||||
return v.nm.MaxFileKey()
|
||||
}
|
||||
|
||||
func (v *Volume) IndexFileSize() uint64 {
|
||||
v.dataFileAccessLock.RLock()
|
||||
defer v.dataFileAccessLock.RUnlock()
|
||||
if v.nm == nil {
|
||||
return 0
|
||||
}
|
||||
return v.nm.IndexFileSize()
|
||||
}
|
||||
|
||||
func (v *Volume) DiskType() types.DiskType {
|
||||
return v.location.DiskType
|
||||
}
|
||||
|
||||
func (v *Volume) SyncToDisk() {
|
||||
v.dataFileAccessLock.Lock()
|
||||
defer v.dataFileAccessLock.Unlock()
|
||||
if v.nm != nil {
|
||||
if err := v.nm.Sync(); err != nil {
|
||||
glog.Warningf("Volume Close fail to sync volume idx %d", v.Id)
|
||||
}
|
||||
}
|
||||
if v.DataBackend != nil {
|
||||
if err := v.DataBackend.Sync(); err != nil {
|
||||
glog.Warningf("Volume Close fail to sync volume %d", v.Id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Close cleanly shuts down this volume
|
||||
func (v *Volume) Close() {
|
||||
// Wait for any in-progress compaction to finish and claim the flag so no
|
||||
// new compaction can start. This must happen BEFORE acquiring
|
||||
// dataFileAccessLock to avoid deadlocking with CommitCompact which holds
|
||||
// the flag while waiting for the lock.
|
||||
for !v.isCompactionInProgress.CompareAndSwap(false, true) {
|
||||
time.Sleep(521 * time.Millisecond)
|
||||
glog.Warningf("Volume Close wait for compaction %d", v.Id)
|
||||
}
|
||||
defer v.isCompactionInProgress.Store(false)
|
||||
|
||||
v.dataFileAccessLock.Lock()
|
||||
defer v.dataFileAccessLock.Unlock()
|
||||
|
||||
v.doClose()
|
||||
}
|
||||
|
||||
// SwapDataBackend atomically replaces the data backend and updates the
|
||||
// remote-tier flag under dataFileAccessLock, closing the old backend. Both tier
|
||||
// directions go through here so hasRemoteFile always matches the live backend:
|
||||
// tier-down passes hasRemoteFile=false (now serving a local .dat), tier-up
|
||||
// passes true. Keeping the swap and the flag under one lock means the heartbeat
|
||||
// never observes a half-swapped backend or a flag that disagrees with it — a
|
||||
// stale-false flag would make doDeleteRequest skip the new .dat's tombstones and
|
||||
// disable the phantom-.dat guard.
|
||||
func (v *Volume) SwapDataBackend(newBackend backend.BackendStorageFile, hasRemoteFile bool) {
|
||||
v.dataFileAccessLock.Lock()
|
||||
defer v.dataFileAccessLock.Unlock()
|
||||
v.swapDataBackendLocked(newBackend, hasRemoteFile)
|
||||
}
|
||||
|
||||
// swapDataBackendLocked is the body of SwapDataBackend for callers that already
|
||||
// hold dataFileAccessLock (e.g. load() reached while CommitCompact holds the
|
||||
// lock). Reusing it from those under-lock paths avoids re-entering the
|
||||
// non-reentrant lock, which would deadlock.
|
||||
func (v *Volume) swapDataBackendLocked(newBackend backend.BackendStorageFile, hasRemoteFile bool) {
|
||||
if v.DataBackend != nil {
|
||||
v.DataBackend.Close()
|
||||
}
|
||||
v.DataBackend = newBackend
|
||||
v.hasRemoteFile.Store(hasRemoteFile)
|
||||
}
|
||||
|
||||
func (v *Volume) doClose() {
|
||||
if v.nm != nil {
|
||||
if err := v.nm.Sync(); err != nil {
|
||||
glog.Warningf("Volume Close fail to sync volume idx %d", v.Id)
|
||||
}
|
||||
v.nm.Close()
|
||||
v.nm = nil
|
||||
}
|
||||
if v.DataBackend != nil {
|
||||
if err := v.DataBackend.Close(); err != nil {
|
||||
glog.Warningf("Volume Close fail to sync volume %d", v.Id)
|
||||
}
|
||||
v.DataBackend = nil
|
||||
stats.VolumeServerVolumeGauge.WithLabelValues(v.Collection, "volume").Dec()
|
||||
}
|
||||
}
|
||||
|
||||
func (v *Volume) NeedToReplicate() bool {
|
||||
return v.ReplicaPlacement.GetCopyCount() > 1
|
||||
}
|
||||
|
||||
// volume is expired if modified time + volume ttl < now
|
||||
// except when volume is empty
|
||||
// or when the volume does not have a ttl
|
||||
// or when volumeSizeLimit is 0 when server just starts
|
||||
func (v *Volume) expired(contentSize uint64, volumeSizeLimit uint64) bool {
|
||||
if volumeSizeLimit == 0 {
|
||||
// skip if we don't know size limit
|
||||
return false
|
||||
}
|
||||
if contentSize <= super_block.SuperBlockSize {
|
||||
return false
|
||||
}
|
||||
if v.Ttl == nil || v.Ttl.Minutes() == 0 {
|
||||
return false
|
||||
}
|
||||
glog.V(2).Infof("volume %d now:%v lastModified:%v", v.Id, time.Now().Unix(), v.lastModifiedTsSeconds)
|
||||
livedMinutes := (time.Now().Unix() - int64(v.lastModifiedTsSeconds)) / 60
|
||||
glog.V(2).Infof("volume %d ttl:%v lived:%v", v.Id, v.Ttl, livedMinutes)
|
||||
if int64(v.Ttl.Minutes()) < livedMinutes {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ExpireAtSec is when this volume's data becomes garbage, counted from its last
|
||||
// write. Counting from the current time instead let every .vif rewrite -- a
|
||||
// read-only mark, a tier upload, an EC encode -- hand an already expiring volume
|
||||
// another full TTL. Zero when the volume has no TTL.
|
||||
func (v *Volume) ExpireAtSec() uint64 {
|
||||
if v.Ttl == nil {
|
||||
return 0
|
||||
}
|
||||
ttlSeconds := v.Ttl.ToSeconds()
|
||||
if ttlSeconds == 0 {
|
||||
return 0
|
||||
}
|
||||
lastWriteSec := v.lastModifiedTsSeconds
|
||||
if lastWriteSec == 0 {
|
||||
lastWriteSec = uint64(time.Now().Unix())
|
||||
}
|
||||
return lastWriteSec + ttlSeconds
|
||||
}
|
||||
|
||||
// wait either maxDelayMinutes or 10% of ttl minutes
|
||||
func (v *Volume) expiredLongEnough(maxDelayMinutes uint32) bool {
|
||||
if v.Ttl == nil || v.Ttl.Minutes() == 0 {
|
||||
return false
|
||||
}
|
||||
removalDelay := v.Ttl.Minutes() / 10
|
||||
if removalDelay > maxDelayMinutes {
|
||||
removalDelay = maxDelayMinutes
|
||||
}
|
||||
|
||||
if uint64(v.Ttl.Minutes()+removalDelay)*60+v.lastModifiedTsSeconds < uint64(time.Now().Unix()) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (v *Volume) collectStatus() (maxFileKey types.NeedleId, datFileSize int64, modTime time.Time, fileCount, deletedCount, deletedSize uint64, ok bool) {
|
||||
v.dataFileAccessLock.RLock()
|
||||
defer v.dataFileAccessLock.RUnlock()
|
||||
|
||||
if v.nm == nil || v.DataBackend == nil {
|
||||
return
|
||||
}
|
||||
|
||||
ok = true
|
||||
|
||||
maxFileKey = v.nm.MaxFileKey()
|
||||
datFileSize, modTime, _ = v.DataBackend.GetStat()
|
||||
fileCount = uint64(v.nm.FileCount())
|
||||
deletedCount = uint64(v.nm.DeletedCount())
|
||||
deletedSize = v.nm.DeletedSize()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// ToVolumeInformationMessage fills into with what the master is told about this
|
||||
// volume, allocating a message when into is nil. A heartbeat that keeps only
|
||||
// the volumes it reports fills the same message for all the rest.
|
||||
func (v *Volume) ToVolumeInformationMessage(into *master_pb.VolumeInformationMessage) (types.NeedleId, *master_pb.VolumeInformationMessage) {
|
||||
|
||||
maxFileKey, volumeSize, modTime, fileCount, deletedCount, deletedSize, ok := v.collectStatus()
|
||||
|
||||
if !ok {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// Detect phantom volumes: the .dat was unlinked from disk but is still held
|
||||
// open as a deleted FD, so the volume keeps serving and heartbeating while no
|
||||
// disk-path operation can ever succeed. Skip remote-tiered volumes, whose .dat
|
||||
// legitimately lives in cloud storage. Only a present .dat is cached for 30s; a
|
||||
// missing one is re-checked every heartbeat so the volume stays suppressed until
|
||||
// the file returns. See github.com/seaweedfs/seaweedfs/issues/10004
|
||||
if fileCount > 0 && !v.HasRemoteFile() {
|
||||
const diskCheckIntervalNs = 30 * int64(time.Second)
|
||||
now := time.Now().UnixNano()
|
||||
if now-v.lastDiskCheckNs.Load() > diskCheckIntervalNs {
|
||||
if _, err := os.Stat(v.FileName(".dat")); os.IsNotExist(err) {
|
||||
glog.Warningf("Volume %d: data file %s missing (held open as deleted FD) - not reporting to master", v.Id, v.FileName(".dat"))
|
||||
return 0, nil
|
||||
}
|
||||
v.lastDiskCheckNs.Store(now)
|
||||
}
|
||||
}
|
||||
|
||||
volumeInfo := into
|
||||
if volumeInfo == nil {
|
||||
volumeInfo = &master_pb.VolumeInformationMessage{}
|
||||
}
|
||||
volumeInfo.Id = uint32(v.Id)
|
||||
volumeInfo.Size = uint64(volumeSize)
|
||||
volumeInfo.Collection = v.Collection
|
||||
volumeInfo.FileCount = fileCount
|
||||
volumeInfo.DeleteCount = deletedCount
|
||||
volumeInfo.DeletedByteCount = deletedSize
|
||||
volumeInfo.ReadOnly, _, volumeInfo.ReadOnlyCanDelete, _ = v.ReadOnlyReasons()
|
||||
volumeInfo.ReplicaPlacement = uint32(v.ReplicaPlacement.Byte())
|
||||
volumeInfo.Version = uint32(v.Version())
|
||||
volumeInfo.Ttl = v.Ttl.ToUint32()
|
||||
volumeInfo.CompactRevision = uint32(v.SuperBlock.CompactionRevision)
|
||||
volumeInfo.ModifiedAtSecond = modTime.Unix()
|
||||
volumeInfo.DiskType = string(v.location.DiskType)
|
||||
volumeInfo.DiskId = v.diskId
|
||||
volumeInfo.RemoteStorageName, volumeInfo.RemoteStorageKey = v.RemoteStorageNameKey()
|
||||
|
||||
return maxFileKey, volumeInfo
|
||||
}
|
||||
|
||||
func (v *Volume) RemoteStorageNameKey() (storageName, storageKey string) {
|
||||
if v.volumeInfo == nil {
|
||||
return
|
||||
}
|
||||
if len(v.volumeInfo.GetFiles()) == 0 {
|
||||
return
|
||||
}
|
||||
return v.volumeInfo.GetFiles()[0].BackendName(), v.volumeInfo.GetFiles()[0].GetKey()
|
||||
}
|
||||
|
||||
func (v *Volume) IsReadOnly() bool {
|
||||
readOnly, _, _, _ := v.ReadOnlyReasons()
|
||||
return readOnly
|
||||
}
|
||||
|
||||
// ReadOnlyReasons reports whether the volume refuses writes and why, reading the
|
||||
// flags once so the reasons cannot disagree with the verdict.
|
||||
func (v *Volume) ReadOnlyReasons() (readOnly, noWriteOrDelete, noWriteCanDelete, diskSpaceLow bool) {
|
||||
v.noWriteLock.RLock()
|
||||
noWriteOrDelete, noWriteCanDelete = v.noWriteOrDelete, v.noWriteCanDelete
|
||||
v.noWriteLock.RUnlock()
|
||||
// The location is attached when the volume joins a disk location, which is
|
||||
// after NewVolume hands it back.
|
||||
diskSpaceLow = v.location != nil && v.location.isDiskSpaceLow.Load()
|
||||
return noWriteOrDelete || noWriteCanDelete || diskSpaceLow, noWriteOrDelete, noWriteCanDelete, diskSpaceLow
|
||||
}
|
||||
|
||||
func (v *Volume) PersistReadOnly(readOnly bool, canDelete bool) error {
|
||||
v.volumeInfoRWLock.Lock()
|
||||
defer v.volumeInfoRWLock.Unlock()
|
||||
prevReadOnly := v.volumeInfo.ReadOnly
|
||||
prevReadOnlyCanDelete := v.volumeInfo.ReadOnlyCanDelete
|
||||
v.volumeInfo.ReadOnly = readOnly
|
||||
v.volumeInfo.ReadOnlyCanDelete = readOnly && canDelete
|
||||
if err := v.SaveVolumeInfo(); err != nil {
|
||||
// A pre-commit failure (write/sync/close/rename) leaves the old
|
||||
// .vif intact, so roll back in-memory state to match it. A
|
||||
// NotCrashDurableError means the rename already committed the
|
||||
// new mode to disk; rolling back would split in-memory state
|
||||
// from the durable file, so keep the new state and propagate.
|
||||
var ndErr *volume_info.NotCrashDurableError
|
||||
if !errors.As(err, &ndErr) {
|
||||
v.volumeInfo.ReadOnly = prevReadOnly
|
||||
v.volumeInfo.ReadOnlyCanDelete = prevReadOnlyCanDelete
|
||||
}
|
||||
return fmt.Errorf("persist volume read-only state: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user