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
237 lines
7.1 KiB
Go
237 lines
7.1 KiB
Go
package storage
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"github.com/seaweedfs/seaweedfs/weed/glog"
|
|
"github.com/seaweedfs/seaweedfs/weed/storage/backend"
|
|
"github.com/seaweedfs/seaweedfs/weed/storage/idx"
|
|
"github.com/seaweedfs/seaweedfs/weed/storage/needle"
|
|
"github.com/seaweedfs/seaweedfs/weed/storage/needle_map"
|
|
"github.com/seaweedfs/seaweedfs/weed/storage/super_block"
|
|
"github.com/seaweedfs/seaweedfs/weed/storage/types"
|
|
"github.com/seaweedfs/seaweedfs/weed/util"
|
|
)
|
|
|
|
// errStopIdxWalk ends an idx.WalkIndexFile early once the answer is known.
|
|
var errStopIdxWalk = errors.New("stop idx walk")
|
|
|
|
// repairIdxHeadTombstones restores the .idx rows that deletes on a tiered
|
|
// read-only volume used to overwrite.
|
|
//
|
|
// The sorted-file needle map opened .idx read-write but never seeded its write
|
|
// position, so a delete wrote its (key, offset 0, tombstone) row at .idx offset
|
|
// 0 and advanced one row at a time instead of appending. Every delete therefore
|
|
// replaced one more row at the front of .idx, and the rows it replaced -- the
|
|
// Put rows indexing the first needles in .dat -- were lost. Reads for those
|
|
// needles return not-found even though .dat still holds them intact.
|
|
//
|
|
// The damage leaves a fingerprint: .idx begins with a run of offset-0
|
|
// tombstones. A healthy .idx never does. Its first row is the Put for the first
|
|
// needle in .dat, and an offset-0 tombstone -- a delete against a tiered volume,
|
|
// which appends no .dat record and so has no extent to point at -- can only ever
|
|
// land at the tail.
|
|
//
|
|
// .idx and .dat grow in lockstep, so the clobbered rows indexed exactly the
|
|
// first N records of .dat. Re-deriving them is a header-only walk over the head
|
|
// of .dat, which stays cheap even when .dat is served from a remote tier.
|
|
//
|
|
// The recovered rows go back in front, where the rows they replace used to sit,
|
|
// and every existing row keeps its relative order behind them. That restores
|
|
// .idx to .dat append order, which several readers lean on: the fingerprint is
|
|
// gone so a later load stops after one row, the last row is the .dat-tail needle
|
|
// again so CheckVolumeDataIntegrity keeps its O(1) path, and
|
|
// BinarySearchByAppendAtNs sees ascending append timestamps.
|
|
func (v *Volume) repairIdxHeadTombstones() (restored int, err error) {
|
|
if v.DataBackend == nil {
|
|
return 0, nil
|
|
}
|
|
version := v.Version()
|
|
if !needle.IsSupportedVersion(version) {
|
|
return 0, nil
|
|
}
|
|
firstNeedleOffset := int64(v.SuperBlock.BlockSize())
|
|
idxFileName := v.FileName(".idx")
|
|
|
|
clobbered, err := idxHeadTombstoneCount(idxFileName)
|
|
if err != nil || clobbered == 0 {
|
|
return 0, err
|
|
}
|
|
|
|
glog.V(0).Infof("volume %d: %s starts with %d offset-0 tombstones, recovering the .idx rows they overwrote from %s",
|
|
v.Id, idxFileName, clobbered, v.FileName(".dat"))
|
|
|
|
lost, order, err := scanDatHead(v.DataBackend, version, firstNeedleOffset, clobbered)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
if err = dropIndexedKeys(idxFileName, lost); err != nil {
|
|
return 0, err
|
|
}
|
|
if len(lost) == 0 {
|
|
return 0, nil
|
|
}
|
|
|
|
var rows []byte
|
|
for _, key := range order {
|
|
nv, ok := lost[key]
|
|
if !ok {
|
|
continue
|
|
}
|
|
rows = append(rows, nv.ToBytes()...)
|
|
restored++
|
|
}
|
|
return restored, prependIdxRows(idxFileName, rows)
|
|
}
|
|
|
|
// idxHeadTombstoneCount reports how many rows at the front of .idx are offset-0
|
|
// tombstones. A healthy .idx answers 0 on its first row.
|
|
func idxHeadTombstoneCount(idxFileName string) (clobbered int, err error) {
|
|
idxFile, err := os.Open(idxFileName)
|
|
if err != nil {
|
|
if os.IsNotExist(err) {
|
|
return 0, nil
|
|
}
|
|
return 0, err
|
|
}
|
|
defer idxFile.Close()
|
|
|
|
err = idx.WalkIndexFile(idxFile, 0, func(_ types.NeedleId, offset types.Offset, size types.Size) error {
|
|
if !offset.IsZero() || !size.IsTombstone() {
|
|
return errStopIdxWalk
|
|
}
|
|
clobbered++
|
|
return nil
|
|
})
|
|
if errors.Is(err, errStopIdxWalk) {
|
|
err = nil
|
|
}
|
|
return clobbered, err
|
|
}
|
|
|
|
// scanDatHead reads the headers of the first limit records of .dat and returns
|
|
// the needles they hold, in .dat order. A record with an invalid size is a
|
|
// delete marker, so the key it names drops out of the result rather than being
|
|
// resurrected.
|
|
func scanDatHead(datBackend backend.BackendStorageFile, version needle.Version, firstNeedleOffset int64, limit int) (map[types.NeedleId]needle_map.NeedleValue, []types.NeedleId, error) {
|
|
scanner := &datHeadScanner{
|
|
limit: limit,
|
|
found: make(map[types.NeedleId]needle_map.NeedleValue),
|
|
}
|
|
if err := ScanVolumeFileFrom(version, datBackend, firstNeedleOffset, scanner); err != nil {
|
|
return nil, nil, fmt.Errorf("scan head of %s: %w", datBackend.Name(), err)
|
|
}
|
|
return scanner.found, scanner.order, nil
|
|
}
|
|
|
|
type datHeadScanner struct {
|
|
limit int
|
|
visited int
|
|
found map[types.NeedleId]needle_map.NeedleValue
|
|
order []types.NeedleId
|
|
}
|
|
|
|
func (s *datHeadScanner) VisitSuperBlock(super_block.SuperBlock) error { return nil }
|
|
|
|
func (s *datHeadScanner) ReadNeedleBody() bool { return false }
|
|
|
|
func (s *datHeadScanner) VisitNeedle(n *needle.Needle, offset int64, _, _ []byte) error {
|
|
if n.Size.IsValid() {
|
|
if _, seen := s.found[n.Id]; !seen {
|
|
s.order = append(s.order, n.Id)
|
|
}
|
|
s.found[n.Id] = needle_map.NeedleValue{Key: n.Id, Offset: types.ToOffset(offset), Size: n.Size}
|
|
} else {
|
|
delete(s.found, n.Id)
|
|
}
|
|
s.visited++
|
|
if s.visited >= s.limit {
|
|
return io.EOF
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// dropIndexedKeys removes every candidate the .idx already names, whether by a
|
|
// Put row or a tombstone. What is left is only what the clobbered rows held.
|
|
func dropIndexedKeys(idxFileName string, candidates map[types.NeedleId]needle_map.NeedleValue) error {
|
|
if len(candidates) == 0 {
|
|
return nil
|
|
}
|
|
idxFile, err := os.Open(idxFileName)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer idxFile.Close()
|
|
|
|
err = idx.WalkIndexFile(idxFile, 0, func(key types.NeedleId, _ types.Offset, _ types.Size) error {
|
|
delete(candidates, key)
|
|
if len(candidates) == 0 {
|
|
return errStopIdxWalk
|
|
}
|
|
return nil
|
|
})
|
|
if errors.Is(err, errStopIdxWalk) {
|
|
err = nil
|
|
}
|
|
return err
|
|
}
|
|
|
|
// prependIdxRows rewrites .idx as rows followed by its current contents,
|
|
// through a temp file and a rename so a crash mid-write never leaves a partial
|
|
// index at the live name.
|
|
func prependIdxRows(idxFileName string, rows []byte) error {
|
|
src, err := os.Open(idxFileName)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer src.Close()
|
|
|
|
srcStat, err := src.Stat()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
tmpFileName := idxFileName + ".tmp"
|
|
dst, err := os.OpenFile(tmpFileName, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, srcStat.Mode().Perm())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
committed := false
|
|
defer func() {
|
|
dst.Close()
|
|
if !committed {
|
|
os.Remove(tmpFileName)
|
|
}
|
|
}()
|
|
|
|
// The rename replaces .idx with this file, so it has to carry the mode the
|
|
// index already had -- O_CREATE alone leaves it at the umask's mercy.
|
|
if err = dst.Chmod(srcStat.Mode().Perm()); err != nil {
|
|
return err
|
|
}
|
|
if _, err = dst.Write(rows); err != nil {
|
|
return err
|
|
}
|
|
if _, err = io.Copy(dst, src); err != nil {
|
|
return err
|
|
}
|
|
if err = dst.Sync(); err != nil {
|
|
return err
|
|
}
|
|
if err = dst.Close(); err != nil {
|
|
return err
|
|
}
|
|
if err = os.Rename(tmpFileName, idxFileName); err != nil {
|
|
return err
|
|
}
|
|
if err = util.FsyncDir(filepath.Dir(idxFileName)); err != nil {
|
|
return err
|
|
}
|
|
committed = true
|
|
return nil
|
|
}
|