Files
seaweedfs/weed/storage/blockvol/config.go
T
Ping QiuandClaude Opus 4.6 9d0ec8efa3 feat: tri-state IOBackend config with explicit logging and CLI flag
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>
2026-03-10 16:02:30 -07:00

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
}