mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-10-15 10:38:35 +02:00
A single syscall.EIO from any read/write/delete set v.lastIoError, and the next CollectHeartbeat then called Volume.Destroy on the replica — removing the .dat/.idx/.vif/.sdx/.ldb/.rdb files. A brief NFS / fabric / controller blip hitting several replicas at once could cascade into removal of the last healthy copy, with no recovery for non-tiered volumes. Now require IoErrorTolerance (3) consecutive EIOs before acting, and on that threshold mark the volume read-only and stop announcing it to the master so re-replication kicks in from healthy peers — never delete the data files. The on-disk copy stays for operator inspection / recovery.
62 lines
1.7 KiB
Go
62 lines
1.7 KiB
Go
package storage
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"syscall"
|
|
"testing"
|
|
)
|
|
|
|
func TestCheckReadWriteErrorTracksConsecutiveEIO(t *testing.T) {
|
|
v := &Volume{}
|
|
|
|
// non-EIO errors must not advance the counter.
|
|
v.checkReadWriteError(errors.New("some other error"))
|
|
if got := v.lastIoErrorCount.Load(); got != 0 {
|
|
t.Fatalf("non-EIO error advanced counter: got %d", got)
|
|
}
|
|
if v.lastIoError != nil {
|
|
t.Fatalf("non-EIO error set lastIoError: %v", v.lastIoError)
|
|
}
|
|
|
|
// each EIO bumps the counter.
|
|
for i := int32(1); i <= 5; i++ {
|
|
v.checkReadWriteError(fmt.Errorf("disk failed: %w", syscall.EIO))
|
|
if got := v.lastIoErrorCount.Load(); got != i {
|
|
t.Fatalf("after %d EIO(s): counter = %d, want %d", i, got, i)
|
|
}
|
|
if v.lastIoError == nil {
|
|
t.Fatalf("after %d EIO(s): lastIoError is nil", i)
|
|
}
|
|
}
|
|
|
|
// a single success resets both fields.
|
|
v.checkReadWriteError(nil)
|
|
if got := v.lastIoErrorCount.Load(); got != 0 {
|
|
t.Fatalf("success did not reset counter: got %d", got)
|
|
}
|
|
if v.lastIoError != nil {
|
|
t.Fatalf("success did not clear lastIoError: %v", v.lastIoError)
|
|
}
|
|
}
|
|
|
|
func TestIoErrorToleranceGate(t *testing.T) {
|
|
v := &Volume{}
|
|
|
|
// below tolerance: do not act.
|
|
for i := 0; i < IoErrorTolerance-1; i++ {
|
|
v.checkReadWriteError(fmt.Errorf("eio: %w", syscall.EIO))
|
|
}
|
|
if v.lastIoErrorCount.Load() >= IoErrorTolerance {
|
|
t.Fatalf("counter %d already crossed tolerance %d after %d errors",
|
|
v.lastIoErrorCount.Load(), IoErrorTolerance, IoErrorTolerance-1)
|
|
}
|
|
|
|
// one more crosses the threshold.
|
|
v.checkReadWriteError(fmt.Errorf("eio: %w", syscall.EIO))
|
|
if got := v.lastIoErrorCount.Load(); got < IoErrorTolerance {
|
|
t.Fatalf("counter %d below tolerance %d after %d errors",
|
|
got, IoErrorTolerance, IoErrorTolerance)
|
|
}
|
|
}
|