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:
505
weed/storage/volume_write.go
Normal file
505
weed/storage/volume_write.go
Normal file
@@ -0,0 +1,505 @@
|
||||
package storage
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/seaweedfs/seaweedfs/weed/glog"
|
||||
"github.com/seaweedfs/seaweedfs/weed/storage/backend"
|
||||
"github.com/seaweedfs/seaweedfs/weed/storage/needle"
|
||||
. "github.com/seaweedfs/seaweedfs/weed/storage/types"
|
||||
)
|
||||
|
||||
var ErrorNotFound = errors.New("not found")
|
||||
var ErrorDeleted = errors.New("already deleted")
|
||||
var ErrorSizeMismatch = errors.New("size mismatch")
|
||||
|
||||
// isFileUnchanged checks whether this needle to write is same as last one.
|
||||
// It requires serialized access in the same volume.
|
||||
func (v *Volume) isFileUnchanged(n *needle.Needle) bool {
|
||||
if v.Ttl.String() != "" {
|
||||
return false
|
||||
}
|
||||
|
||||
nv, ok := v.nm.Get(n.Id)
|
||||
if ok && !nv.Offset.IsZero() && nv.Size.IsValid() {
|
||||
oldNeedle := new(needle.Needle)
|
||||
err := oldNeedle.ReadData(v.DataBackend, nv.Offset.ToActualOffset(), nv.Size, v.Version())
|
||||
if err != nil {
|
||||
glog.V(0).Infof("Failed to check updated file at offset %d size %d: %v", nv.Offset.ToActualOffset(), nv.Size, err)
|
||||
return false
|
||||
}
|
||||
if oldNeedle.Cookie == n.Cookie && oldNeedle.Checksum == n.Checksum && bytes.Equal(oldNeedle.Data, n.Data) {
|
||||
n.DataSize = oldNeedle.DataSize
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
var ErrVolumeNotEmpty = fmt.Errorf("volume not empty")
|
||||
|
||||
// Destroy removes everything related to this volume. When keepRemoteData is
|
||||
// true the cloud-tier object backing the volume is left intact — used by
|
||||
// moves where another server is taking over the same .vif.
|
||||
func (v *Volume) Destroy(onlyEmpty bool, keepRemoteData bool) (err error) {
|
||||
v.dataFileAccessLock.Lock()
|
||||
defer v.dataFileAccessLock.Unlock()
|
||||
|
||||
if onlyEmpty {
|
||||
isEmpty, e := v.doIsEmpty()
|
||||
if e != nil {
|
||||
err = fmt.Errorf("failed to read isEmpty %v", e)
|
||||
return
|
||||
}
|
||||
if !isEmpty {
|
||||
err = ErrVolumeNotEmpty
|
||||
return
|
||||
}
|
||||
}
|
||||
if !v.isCompactionInProgress.CompareAndSwap(false, true) {
|
||||
err = fmt.Errorf("volume %d is compacting", v.Id)
|
||||
return
|
||||
}
|
||||
v.stopWorker()
|
||||
if !keepRemoteData {
|
||||
storageName, storageKey := v.RemoteStorageNameKey()
|
||||
if v.HasRemoteFile() && storageName != "" && storageKey != "" {
|
||||
if backendStorage, found := backend.BackendStorages[storageName]; found {
|
||||
backendStorage.DeleteFile(storageKey)
|
||||
}
|
||||
}
|
||||
}
|
||||
// A regular volume and an EC volume for the same id share <base>.vif. When
|
||||
// EC artefacts coexist on this disk (e.g. shards distributed onto a source
|
||||
// replica before it is deleted), keep the .vif so removing the regular
|
||||
// volume does not strip the EC volume's info file.
|
||||
keepVif := v.sharesVifWithEcVolume()
|
||||
v.doClose()
|
||||
removeVolumeFiles(v.DataFileName(), keepVif)
|
||||
removeVolumeFiles(v.IndexFileName(), keepVif)
|
||||
return
|
||||
}
|
||||
|
||||
// sharesVifWithEcVolume reports whether an EC volume for this volume id lives
|
||||
// on the same disk, in which case its .vif is the same file as the regular
|
||||
// volume's and must outlive the regular volume's deletion.
|
||||
func (v *Volume) sharesVifWithEcVolume() bool {
|
||||
if v.location == nil {
|
||||
return false
|
||||
}
|
||||
if _, found := v.location.FindEcVolume(v.Id); found {
|
||||
return true
|
||||
}
|
||||
return v.location.HasEcxFileOnDisk(v.Collection, v.Id)
|
||||
}
|
||||
|
||||
func removeVolumeFiles(filename string, keepVif bool) {
|
||||
// .dat/.idx removals log at V(0) so destructive calls are traceable.
|
||||
deleteAndLog := func(ext string) {
|
||||
fullFilename := filename + "." + ext
|
||||
st, statErr := os.Stat(fullFilename)
|
||||
err := os.RemoveAll(fullFilename)
|
||||
if err != nil {
|
||||
glog.V(0).Infof("failed to remove volume file %s: %s", fullFilename, err)
|
||||
return
|
||||
}
|
||||
if statErr == nil && (ext == "dat" || ext == "idx") {
|
||||
glog.Infof("removed volume file %s (size=%d)", fullFilename, st.Size())
|
||||
}
|
||||
}
|
||||
deleteAndLog("dat")
|
||||
deleteAndLog("idx")
|
||||
if !keepVif {
|
||||
deleteAndLog("vif")
|
||||
}
|
||||
// sorted index file
|
||||
deleteAndLog("sdx")
|
||||
// compaction
|
||||
deleteAndLog("cpd")
|
||||
deleteAndLog("cpx")
|
||||
// compaction commit marker
|
||||
deleteAndLog("cpc")
|
||||
// level db index file
|
||||
deleteAndLog("ldb")
|
||||
// redb index file (Rust volume server)
|
||||
deleteAndLog("rdb")
|
||||
// marker for damaged or incomplete volume
|
||||
deleteAndLog("note")
|
||||
}
|
||||
|
||||
// asyncRequestAppend queues a request for the batch worker, starting it on the
|
||||
// first one. It reports false for a destroyed volume, so the caller writes
|
||||
// inline rather than wait on a worker that will never answer.
|
||||
func (v *Volume) asyncRequestAppend(request *needle.AsyncRequest) bool {
|
||||
requests := v.startWorker()
|
||||
if requests == nil {
|
||||
return false
|
||||
}
|
||||
requests <- request
|
||||
return true
|
||||
}
|
||||
|
||||
func (v *Volume) syncWrite(n *needle.Needle, checkCookie bool, fsync bool) (offset uint64, size Size, isUnchanged bool, err error) {
|
||||
// glog.V(4).Infof("writing needle %s", needle.NewFileIdFromNeedle(v.Id, n).String())
|
||||
v.dataFileAccessLock.Lock()
|
||||
defer v.dataFileAccessLock.Unlock()
|
||||
|
||||
// A caller can still hold the volume after it was closed or destroyed, which
|
||||
// leaves both of these nil. Refuse the write rather than dereference them.
|
||||
if v.nm == nil || v.DataBackend == nil {
|
||||
return 0, 0, false, fmt.Errorf("volume %d is closed", v.Id)
|
||||
}
|
||||
|
||||
if !fsync {
|
||||
return v.doWriteRequest(n, checkCookie)
|
||||
}
|
||||
|
||||
end, _, statErr := v.DataBackend.GetStat()
|
||||
if statErr != nil {
|
||||
return 0, 0, false, fmt.Errorf("cannot read current volume position: %v", statErr)
|
||||
}
|
||||
priorOffset, priorSize, hasPrior := Offset{}, Size(0), false
|
||||
if nv, found := v.nm.Get(n.Id); found {
|
||||
priorOffset, priorSize, hasPrior = nv.Offset, nv.Size, true
|
||||
}
|
||||
|
||||
offset, size, isUnchanged, err = v.doWriteRequest(n, checkCookie)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if syncErr := v.DataBackend.Sync(); syncErr != nil {
|
||||
v.checkReadWriteError(syncErr)
|
||||
if !isUnchanged {
|
||||
v.rollbackUnflushedWrite(n, offset, end, priorOffset, priorSize, hasPrior)
|
||||
}
|
||||
return 0, 0, false, syncErr
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// rollbackUnflushedWrite undoes an append whose fsync failed: the bytes are not
|
||||
// data we can vouch for, so they come back off the .dat and the needle map goes
|
||||
// back to what it pointed at before, rather than at an offset past the new end.
|
||||
func (v *Volume) rollbackUnflushedWrite(n *needle.Needle, offset uint64, end int64, priorOffset Offset, priorSize Size, hasPrior bool) {
|
||||
if te := v.DataBackend.Truncate(end); te != nil {
|
||||
glog.V(0).Infof("Failed to truncate %s back to %d with error: %v", v.DataBackend.Name(), end, te)
|
||||
}
|
||||
current, found := v.nm.Get(n.Id)
|
||||
if !found || current.Offset.ToActualOffset() != int64(offset) {
|
||||
// doWriteRequest kept an existing mapping at a higher offset
|
||||
return
|
||||
}
|
||||
var err error
|
||||
if hasPrior {
|
||||
err = v.nm.Put(n.Id, priorOffset, priorSize)
|
||||
} else {
|
||||
err = v.nm.Delete(n.Id, ToOffset(int64(offset)))
|
||||
}
|
||||
if err != nil {
|
||||
glog.V(0).Infof("Failed to roll back the index of needle %d in volume %d: %v", n.Id, v.Id, err)
|
||||
}
|
||||
}
|
||||
|
||||
// writeNeedle2 appends a needle. A durable write normally goes through the
|
||||
// async batch worker, which fsyncs once for the whole batch; while the server
|
||||
// is stopping the worker is winding down, so it is flushed inline instead. Both
|
||||
// paths only return once the .dat is on disk.
|
||||
func (v *Volume) writeNeedle2(n *needle.Needle, checkCookie bool, fsync bool, isStopping bool) (offset uint64, size Size, isUnchanged bool, err error) {
|
||||
// glog.V(4).Infof("writing needle %s", needle.NewFileIdFromNeedle(v.Id, n).String())
|
||||
if n.Ttl == needle.EMPTY_TTL && v.Ttl != needle.EMPTY_TTL {
|
||||
n.SetHasTtl()
|
||||
n.Ttl = v.Ttl
|
||||
}
|
||||
|
||||
if !fsync || isStopping {
|
||||
return v.syncWrite(n, checkCookie, fsync)
|
||||
} else {
|
||||
asyncRequest := needle.NewAsyncRequest(n, true)
|
||||
// using len(n.Data) here instead of n.Size before n.Size is populated in n.Append()
|
||||
asyncRequest.ActualSize = needle.GetActualSize(Size(len(n.Data)), v.Version())
|
||||
|
||||
if !v.asyncRequestAppend(asyncRequest) {
|
||||
return v.syncWrite(n, checkCookie, fsync)
|
||||
}
|
||||
offset, _, isUnchanged, err = asyncRequest.WaitComplete()
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (v *Volume) doWriteRequest(n *needle.Needle, checkCookie bool) (offset uint64, size Size, isUnchanged bool, err error) {
|
||||
// glog.V(4).Infof("writing needle %s", needle.NewFileIdFromNeedle(v.Id, n).String())
|
||||
if v.isFileUnchanged(n) {
|
||||
size = Size(n.DataSize)
|
||||
isUnchanged = true
|
||||
return
|
||||
}
|
||||
|
||||
// check whether existing needle cookie matches
|
||||
nv, ok := v.nm.Get(n.Id)
|
||||
if ok {
|
||||
existingNeedle, _, _, existingNeedleReadErr := needle.ReadNeedleHeader(v.DataBackend, v.Version(), nv.Offset.ToActualOffset())
|
||||
if existingNeedleReadErr != nil {
|
||||
err = fmt.Errorf("reading existing needle: %w", existingNeedleReadErr)
|
||||
return
|
||||
}
|
||||
if n.Cookie == 0 && !checkCookie {
|
||||
// this is from batch deletion, and read back again when tailing a remote volume
|
||||
// which only happens when checkCookie == false and fsync == false
|
||||
n.Cookie = existingNeedle.Cookie
|
||||
}
|
||||
if existingNeedle.Cookie != n.Cookie {
|
||||
glog.V(0).Infof("write cookie mismatch: existing %s, new %s",
|
||||
needle.NewFileIdFromNeedle(v.Id, existingNeedle), needle.NewFileIdFromNeedle(v.Id, n))
|
||||
err = fmt.Errorf("mismatching cookie %x", n.Cookie)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// append to dat file
|
||||
n.UpdateAppendAtNs(v.lastAppendAtNs)
|
||||
var actualSize int64
|
||||
offset, size, actualSize, err = n.Append(v.DataBackend, v.Version())
|
||||
v.checkReadWriteError(err)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("append to volume %d size %d actualSize %d: %v", v.Id, size, actualSize, err)
|
||||
return
|
||||
}
|
||||
v.lastAppendAtNs = n.AppendAtNs
|
||||
|
||||
// add to needle map
|
||||
if !ok || uint64(nv.Offset.ToActualOffset()) < offset {
|
||||
if err = v.nm.Put(n.Id, ToOffset(int64(offset)), n.Size); err != nil {
|
||||
err = fmt.Errorf("index needle %d of volume %d at offset %d: %w", n.Id, v.Id, offset, err)
|
||||
glog.V(0).Info(err)
|
||||
}
|
||||
}
|
||||
if v.lastModifiedTsSeconds < n.LastModified {
|
||||
v.lastModifiedTsSeconds = n.LastModified
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (v *Volume) syncDelete(n *needle.Needle) (Size, error) {
|
||||
// glog.V(4).Infof("delete needle %s", needle.NewFileIdFromNeedle(v.Id, n).String())
|
||||
v.dataFileAccessLock.Lock()
|
||||
defer v.dataFileAccessLock.Unlock()
|
||||
|
||||
if v.nm == nil {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
return v.doDeleteRequest(n)
|
||||
}
|
||||
|
||||
func (v *Volume) deleteNeedle2(n *needle.Needle) (Size, error) {
|
||||
// todo: delete info is always appended no fsync, it may need fsync in future
|
||||
fsync := false
|
||||
|
||||
if !fsync {
|
||||
return v.syncDelete(n)
|
||||
} else {
|
||||
asyncRequest := needle.NewAsyncRequest(n, false)
|
||||
asyncRequest.ActualSize = needle.GetActualSize(0, v.Version())
|
||||
|
||||
if !v.asyncRequestAppend(asyncRequest) {
|
||||
return v.syncDelete(n)
|
||||
}
|
||||
_, size, _, err := asyncRequest.WaitComplete()
|
||||
|
||||
return Size(size), err
|
||||
}
|
||||
}
|
||||
|
||||
func (v *Volume) doDeleteRequest(n *needle.Needle) (Size, error) {
|
||||
glog.V(4).Infof("delete needle %s", needle.NewFileIdFromNeedle(v.Id, n).String())
|
||||
nv, ok := v.nm.Get(n.Id)
|
||||
// fmt.Println("key", n.Id, "volume offset", nv.Offset, "data_size", n.Size, "cached size", nv.Size)
|
||||
if ok && !nv.Size.IsDeleted() {
|
||||
var offset uint64
|
||||
var err error
|
||||
size := nv.Size
|
||||
if !v.HasRemoteFile() {
|
||||
n.Data = nil
|
||||
n.UpdateAppendAtNs(v.lastAppendAtNs)
|
||||
offset, _, _, err = n.Append(v.DataBackend, v.Version())
|
||||
v.checkReadWriteError(err)
|
||||
if err != nil {
|
||||
return size, err
|
||||
}
|
||||
}
|
||||
v.lastAppendAtNs = n.AppendAtNs
|
||||
if err = v.nm.Delete(n.Id, ToOffset(int64(offset))); err != nil {
|
||||
return size, err
|
||||
}
|
||||
return size, err
|
||||
}
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// startWorker returns the volume's batch-write channel, creating it and its
|
||||
// goroutine on first use, and nil once stopWorker has run.
|
||||
func (v *Volume) startWorker() chan *needle.AsyncRequest {
|
||||
v.asyncWorkerLock.Lock()
|
||||
defer v.asyncWorkerLock.Unlock()
|
||||
if v.asyncWorkerClosed {
|
||||
return nil
|
||||
}
|
||||
if v.asyncRequestsChan != nil {
|
||||
return v.asyncRequestsChan
|
||||
}
|
||||
requests := make(chan *needle.AsyncRequest, 128)
|
||||
v.asyncRequestsChan = requests
|
||||
go func() {
|
||||
chanClosed := false
|
||||
for {
|
||||
// chan closed. go thread will exit
|
||||
if chanClosed {
|
||||
break
|
||||
}
|
||||
currentRequests := make([]*needle.AsyncRequest, 0, 128)
|
||||
currentBytesToWrite := int64(0)
|
||||
for {
|
||||
request, ok := <-requests
|
||||
// volume may be closed
|
||||
if !ok {
|
||||
chanClosed = true
|
||||
break
|
||||
}
|
||||
if MaxPossibleVolumeSize < v.ContentSize()+uint64(currentBytesToWrite+request.ActualSize) {
|
||||
request.Complete(0, 0, false,
|
||||
fmt.Errorf("volume size limit %d exceeded! current size is %d", MaxPossibleVolumeSize, v.ContentSize()))
|
||||
break
|
||||
}
|
||||
currentRequests = append(currentRequests, request)
|
||||
currentBytesToWrite += request.ActualSize
|
||||
// submit at most 4M bytes or 128 requests at one time to decrease request delay.
|
||||
// it also need to break if there is no data in channel to avoid io hang.
|
||||
if currentBytesToWrite >= 4*1024*1024 || len(currentRequests) >= 128 || len(requests) == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if len(currentRequests) == 0 {
|
||||
continue
|
||||
}
|
||||
v.dataFileAccessLock.Lock()
|
||||
end, e := int64(0), error(nil)
|
||||
if v.nm == nil || v.DataBackend == nil {
|
||||
e = fmt.Errorf("volume %d is closed", v.Id)
|
||||
} else {
|
||||
end, _, e = v.DataBackend.GetStat()
|
||||
}
|
||||
if e != nil {
|
||||
for i := 0; i < len(currentRequests); i++ {
|
||||
currentRequests[i].Complete(0, 0, false,
|
||||
fmt.Errorf("cannot read current volume position: %v", e))
|
||||
}
|
||||
v.dataFileAccessLock.Unlock()
|
||||
continue
|
||||
}
|
||||
|
||||
for i := 0; i < len(currentRequests); i++ {
|
||||
if currentRequests[i].IsWriteRequest {
|
||||
offset, size, isUnchanged, err := v.doWriteRequest(currentRequests[i].N, true)
|
||||
currentRequests[i].UpdateResult(offset, uint64(size), isUnchanged, err)
|
||||
} else {
|
||||
size, err := v.doDeleteRequest(currentRequests[i].N)
|
||||
currentRequests[i].UpdateResult(0, uint64(size), false, err)
|
||||
}
|
||||
}
|
||||
|
||||
// if sync error, data is not reliable, we should mark the completed request as fail and rollback
|
||||
if err := v.DataBackend.Sync(); err != nil {
|
||||
// todo: this may generate dirty data or cause data inconsistent, may be weed need to panic?
|
||||
if te := v.DataBackend.Truncate(end); te != nil {
|
||||
glog.V(0).Infof("Failed to truncate %s back to %d with error: %v", v.DataBackend.Name(), end, te)
|
||||
}
|
||||
for i := 0; i < len(currentRequests); i++ {
|
||||
if currentRequests[i].IsSucceed() {
|
||||
currentRequests[i].UpdateResult(0, 0, false, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for i := 0; i < len(currentRequests); i++ {
|
||||
currentRequests[i].Submit()
|
||||
}
|
||||
v.dataFileAccessLock.Unlock()
|
||||
}
|
||||
}()
|
||||
return requests
|
||||
}
|
||||
|
||||
// stopWorker closes the batch-write channel so the worker drains what is queued
|
||||
// and exits. It stays closed: a destroyed volume takes no more writes.
|
||||
func (v *Volume) stopWorker() {
|
||||
v.asyncWorkerLock.Lock()
|
||||
defer v.asyncWorkerLock.Unlock()
|
||||
if v.asyncWorkerClosed {
|
||||
return
|
||||
}
|
||||
v.asyncWorkerClosed = true
|
||||
if v.asyncRequestsChan != nil {
|
||||
close(v.asyncRequestsChan)
|
||||
v.asyncRequestsChan = nil
|
||||
}
|
||||
}
|
||||
|
||||
func (v *Volume) WriteNeedleBlob(needleId NeedleId, needleBlob []byte, size Size) error {
|
||||
|
||||
v.dataFileAccessLock.Lock()
|
||||
defer v.dataFileAccessLock.Unlock()
|
||||
|
||||
// nm.Put on a read-only volume fails only after the blob is appended to .dat.
|
||||
if v.IsReadOnly() {
|
||||
return fmt.Errorf("volume %d is read only", v.Id)
|
||||
}
|
||||
|
||||
// size indexes the needle and places the v3 append timestamp, so a caller using
|
||||
// the payload-only DataSize corrupts both, silently until the needle is read back.
|
||||
if len(needleBlob) < NeedleHeaderSize {
|
||||
return fmt.Errorf("needle %d blob of %d bytes is shorter than a needle header", needleId, len(needleBlob))
|
||||
}
|
||||
var blobHeader needle.Needle
|
||||
blobHeader.ParseNeedleHeader(needleBlob)
|
||||
if blobHeader.Size != size {
|
||||
return fmt.Errorf("needle %d size %d does not match its blob header size %d", needleId, size, blobHeader.Size)
|
||||
}
|
||||
|
||||
if MaxPossibleVolumeSize < v.nm.ContentSize()+uint64(len(needleBlob)) {
|
||||
return fmt.Errorf("volume size limit %d exceeded! current size is %d", MaxPossibleVolumeSize, v.nm.ContentSize())
|
||||
}
|
||||
|
||||
nv, ok := v.nm.Get(needleId)
|
||||
if ok && nv.Size == size {
|
||||
oldNeedle := new(needle.Needle)
|
||||
err := oldNeedle.ReadData(v.DataBackend, nv.Offset.ToActualOffset(), nv.Size, v.Version())
|
||||
if err == nil {
|
||||
newNeedle := new(needle.Needle)
|
||||
err = newNeedle.ReadBytes(needleBlob, nv.Offset.ToActualOffset(), size, v.Version())
|
||||
if err == nil && oldNeedle.Cookie == newNeedle.Cookie && oldNeedle.Checksum == newNeedle.Checksum && bytes.Equal(oldNeedle.Data, newNeedle.Data) {
|
||||
glog.V(0).Infof("needle %v already exists", needleId)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
appendAtNs := needle.GetAppendAtNs(v.lastAppendAtNs)
|
||||
offset, err := needle.WriteNeedleBlob(v.DataBackend, needleBlob, size, appendAtNs, v.Version())
|
||||
|
||||
v.checkReadWriteError(err)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v.lastAppendAtNs = appendAtNs
|
||||
|
||||
// add to needle map
|
||||
if err = v.nm.Put(needleId, ToOffset(int64(offset)), size); err != nil {
|
||||
err = fmt.Errorf("index needle %d of volume %d at offset %d: %w", needleId, v.Id, offset, err)
|
||||
glog.V(0).Info(err)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
Reference in New Issue
Block a user