Documentation
¶
Overview ¶
Package testfs provides a fault-injection wrapper around *os.File for use in crash-safety and durability tests of WAL, snapshot, and checkpoint paths.
A FaultFile is created via New and honours the fault modes configured in Faults:
- [Faults.FailWritesAfterBytes] — returns an error once the cumulative bytes written reaches the threshold (simulates a partial write or disk failure mid-frame).
- [Faults.ReturnENOSPC] — makes every Write call return syscall.ENOSPC regardless of bytes written.
- [Faults.FsyncDelay] — sleeps for the given duration before each Sync call (simulates a slow or stalled fsync).
- [Faults.CorruptOnRead] — when non-nil, is called with the current file offset and the number of bytes about to be read; returning true flips the first byte of the result (simulates a bit-flip or CRC corruption at the given position).
FaultFile implements File, the minimal filesystem interface accepted by store/wal.OpenWith and store/snapshot write paths. File is purposely narrow so it can be satisfied by both *os.File and *FaultFile without importing "os" in tests.
Concurrency: FaultFile is safe for concurrent Read/Write/Seek/ Sync/Truncate/Close calls; all mutations serialise on an internal mutex. The underlying *os.File's own locking therefore plays no role; the wrapper is the serialising layer.
Index ¶
- Variables
- func IsENOSPC(err error) bool
- type FaultFile
- func (ff *FaultFile) BudgetExhausted() bool
- func (ff *FaultFile) Close() error
- func (ff *FaultFile) Read(p []byte) (int, error)
- func (ff *FaultFile) ResetWritten()
- func (ff *FaultFile) Seek(offset int64, whence int) (int64, error)
- func (ff *FaultFile) Sync() error
- func (ff *FaultFile) Truncate(size int64) error
- func (ff *FaultFile) Unwrap() *os.File
- func (ff *FaultFile) Write(p []byte) (int, error)
- func (ff *FaultFile) Written() int64
- type Faults
- type File
Constants ¶
This section is empty.
Variables ¶
var ErrPartialWrite = fmt.Errorf("testfs: write budget exhausted: %w", io.ErrShortWrite)
ErrPartialWrite is returned by Write once [Faults.FailWritesAfterBytes] has been reached. It wraps io.ErrShortWrite so callers that already handle short writes behave correctly.
Functions ¶
Types ¶
type FaultFile ¶
type FaultFile struct {
// contains filtered or unexported fields
}
FaultFile wraps an *os.File with configurable fault injection. Zero-value is invalid; always create via New.
FaultFile is safe for concurrent use; all operations are serialised on an internal mutex.
func New ¶
New opens or creates the file at path (flags: O_RDWR|O_CREATE) with the given Faults configuration.
func Wrap ¶
Wrap creates a FaultFile over an already-open *os.File. The caller must not use f directly after this call; FaultFile takes exclusive ownership.
func (*FaultFile) BudgetExhausted ¶
BudgetExhausted reports whether the FailWritesAfterBytes budget has been reached.
func (*FaultFile) Read ¶
Read implements io.Reader. It honours Faults.CorruptOnRead by flipping the MSB of the first byte when the callback returns true for the current offset and read length.
func (*FaultFile) ResetWritten ¶
func (ff *FaultFile) ResetWritten()
ResetWritten resets the cumulative-bytes counter and re-enables writes after a previous FailWritesAfterBytes fault. This allows a test to confirm a partial-write scenario and then continue writing to the same file for a second phase.
func (*FaultFile) Sync ¶
Sync flushes to durable storage. It sleeps for Faults.FsyncDelay before delegating to the OS.
func (*FaultFile) Unwrap ¶
Unwrap returns the underlying *os.File. Callers must not use the raw file concurrently with FaultFile methods.
type Faults ¶
type Faults struct {
// FailWritesAfterBytes causes Write to fail once the cumulative
// bytes written to the file reaches this value. The partial write
// up to the threshold is permitted; subsequent writes return
// [ErrPartialWrite]. Zero disables this mode.
FailWritesAfterBytes int64
// ReturnENOSPC causes every Write call to return [syscall.ENOSPC]
// regardless of the current write budget.
ReturnENOSPC bool
// FsyncDelay inserts a sleep of this duration before each Sync
// call. Zero disables the delay.
FsyncDelay time.Duration
// CorruptOnRead, when non-nil, is called with the current file
// offset and the number of bytes about to be read. Returning true
// flips the MSB of the first byte in the result buffer to simulate
// a bit-flip or CRC-corrupting storage error.
CorruptOnRead func(offset, n int64) bool
}
Faults configures the injected failure modes for a FaultFile. The zero value disables all fault injection (the wrapper is a transparent pass-through).
type File ¶
type File interface {
io.ReadWriter
io.Seeker
// Sync flushes the OS write buffer to durable storage (equivalent
// to fsync(2)).
Sync() error
// Truncate resizes the file to size bytes. If size < current
// length, the suffix is discarded; if size > current length, the
// file is extended with zero bytes (implementation-defined).
Truncate(size int64) error
// Close releases any associated OS resources.
Close() error
}
File is the minimal filesystem interface used by store/wal and store/snapshot for write paths. *os.File and *FaultFile both implement this interface so production code and tests can accept either without conditional compilation.
File is safe for concurrent use by multiple goroutines (both *os.File and *FaultFile serialise their operations internally).