mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-09-19 22:44:18 +00:00
Replace UseIOUring bool with IOBackend IOBackendMode (tri-state): - "standard" (default): sequential pread/pwrite/fdatasync - "auto": try io_uring, fall back to standard with warning log - "io_uring": require io_uring, fail startup if unavailable NewIOUring now returns ErrIOUringUnavailable instead of silently falling back — callers decide whether to fail or fall back based on the requested mode. All mode transitions are logged: io backend: requested=auto selected=standard reason=... io backend: requested=io_uring selected=io_uring CLI: --io-backend=standard|auto|io_uring added to iscsi-target. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
128 lines
5.0 KiB
Go
128 lines
5.0 KiB
Go
package blockvol
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"time"
|
|
)
|
|
|
|
// BlockVolConfig configures tunable parameters for a BlockVol engine.
|
|
// A zero-value config is valid and will use defaults via applyDefaults().
|
|
type BlockVolConfig struct {
|
|
GroupCommitMaxDelay time.Duration // max wait before flushing a partial batch (default 1ms)
|
|
GroupCommitMaxBatch int // flush immediately when this many waiters (default 64)
|
|
GroupCommitLowWatermark int // skip delay if fewer than this many pending (default 4)
|
|
WALPressureThreshold float64 // fraction of WAL used that triggers urgent flush (default 0.8)
|
|
WALFullTimeout time.Duration // max retry time when WAL is full (default 5s)
|
|
FlushInterval time.Duration // flusher periodic interval (default 100ms)
|
|
DirtyMapShards int // number of dirty map shards, must be power-of-2 (default 256)
|
|
WALSoftWatermark float64 // WAL fraction above which writes begin throttling (default 0.7)
|
|
WALHardWatermark float64 // WAL fraction above which writes block until drain (default 0.9)
|
|
WALMaxConcurrentWrites int // max concurrent writers in WAL append path (default 16)
|
|
IOBackend IOBackendMode // flusher I/O backend: "auto", "standard", "io_uring" (default "standard")
|
|
}
|
|
|
|
// IOBackendMode selects the batch I/O backend for the flusher.
|
|
type IOBackendMode string
|
|
|
|
const (
|
|
// IOBackendStandard uses sequential pread/pwrite/fdatasync (default).
|
|
IOBackendStandard IOBackendMode = "standard"
|
|
|
|
// IOBackendAuto uses io_uring if available, falls back to standard.
|
|
// Logs which backend was selected.
|
|
IOBackendAuto IOBackendMode = "auto"
|
|
|
|
// IOBackendIOUring requires io_uring. Fails volume open/create if unavailable.
|
|
// Use for benchmarking to guarantee the io_uring path is active.
|
|
IOBackendIOUring IOBackendMode = "io_uring"
|
|
)
|
|
|
|
// DefaultConfig returns a BlockVolConfig with production defaults.
|
|
func DefaultConfig() BlockVolConfig {
|
|
return BlockVolConfig{
|
|
GroupCommitMaxDelay: 1 * time.Millisecond,
|
|
GroupCommitMaxBatch: 64,
|
|
GroupCommitLowWatermark: 4,
|
|
WALPressureThreshold: 0.8,
|
|
WALFullTimeout: 5 * time.Second,
|
|
FlushInterval: 100 * time.Millisecond,
|
|
DirtyMapShards: 256,
|
|
WALSoftWatermark: 0.7,
|
|
WALHardWatermark: 0.9,
|
|
WALMaxConcurrentWrites: 16,
|
|
}
|
|
}
|
|
|
|
// applyDefaults fills zero-value fields with production defaults.
|
|
func (c *BlockVolConfig) applyDefaults() {
|
|
d := DefaultConfig()
|
|
if c.GroupCommitMaxDelay == 0 {
|
|
c.GroupCommitMaxDelay = d.GroupCommitMaxDelay
|
|
}
|
|
if c.GroupCommitMaxBatch == 0 {
|
|
c.GroupCommitMaxBatch = d.GroupCommitMaxBatch
|
|
}
|
|
if c.GroupCommitLowWatermark == 0 {
|
|
c.GroupCommitLowWatermark = d.GroupCommitLowWatermark
|
|
}
|
|
if c.WALPressureThreshold == 0 {
|
|
c.WALPressureThreshold = d.WALPressureThreshold
|
|
}
|
|
if c.WALFullTimeout == 0 {
|
|
c.WALFullTimeout = d.WALFullTimeout
|
|
}
|
|
if c.FlushInterval == 0 {
|
|
c.FlushInterval = d.FlushInterval
|
|
}
|
|
if c.DirtyMapShards == 0 {
|
|
c.DirtyMapShards = d.DirtyMapShards
|
|
}
|
|
if c.WALSoftWatermark == 0 {
|
|
c.WALSoftWatermark = d.WALSoftWatermark
|
|
}
|
|
if c.WALHardWatermark == 0 {
|
|
c.WALHardWatermark = d.WALHardWatermark
|
|
}
|
|
if c.WALMaxConcurrentWrites == 0 {
|
|
c.WALMaxConcurrentWrites = d.WALMaxConcurrentWrites
|
|
}
|
|
}
|
|
|
|
var errInvalidConfig = errors.New("blockvol: invalid config")
|
|
|
|
// Validate checks that config values are within acceptable ranges.
|
|
func (c *BlockVolConfig) Validate() error {
|
|
if c.DirtyMapShards <= 0 || (c.DirtyMapShards&(c.DirtyMapShards-1)) != 0 {
|
|
return fmt.Errorf("%w: DirtyMapShards must be a positive power-of-2, got %d", errInvalidConfig, c.DirtyMapShards)
|
|
}
|
|
if c.WALPressureThreshold <= 0 || c.WALPressureThreshold > 1 {
|
|
return fmt.Errorf("%w: WALPressureThreshold must be in (0,1], got %f", errInvalidConfig, c.WALPressureThreshold)
|
|
}
|
|
if c.GroupCommitMaxDelay <= 0 {
|
|
return fmt.Errorf("%w: GroupCommitMaxDelay must be positive, got %v", errInvalidConfig, c.GroupCommitMaxDelay)
|
|
}
|
|
if c.GroupCommitMaxBatch <= 0 {
|
|
return fmt.Errorf("%w: GroupCommitMaxBatch must be positive, got %d", errInvalidConfig, c.GroupCommitMaxBatch)
|
|
}
|
|
if c.GroupCommitLowWatermark < 0 {
|
|
return fmt.Errorf("%w: GroupCommitLowWatermark must be >= 0, got %d", errInvalidConfig, c.GroupCommitLowWatermark)
|
|
}
|
|
if c.WALFullTimeout <= 0 {
|
|
return fmt.Errorf("%w: WALFullTimeout must be positive, got %v", errInvalidConfig, c.WALFullTimeout)
|
|
}
|
|
if c.FlushInterval <= 0 {
|
|
return fmt.Errorf("%w: FlushInterval must be positive, got %v", errInvalidConfig, c.FlushInterval)
|
|
}
|
|
if c.WALSoftWatermark <= 0 || c.WALSoftWatermark >= 1 {
|
|
return fmt.Errorf("%w: WALSoftWatermark must be in (0,1), got %f", errInvalidConfig, c.WALSoftWatermark)
|
|
}
|
|
if c.WALHardWatermark <= c.WALSoftWatermark || c.WALHardWatermark > 1 {
|
|
return fmt.Errorf("%w: WALHardWatermark must be in (SoftWatermark,1], got %f", errInvalidConfig, c.WALHardWatermark)
|
|
}
|
|
if c.WALMaxConcurrentWrites <= 0 {
|
|
return fmt.Errorf("%w: WALMaxConcurrentWrites must be positive, got %d", errInvalidConfig, c.WALMaxConcurrentWrites)
|
|
}
|
|
return nil
|
|
}
|