Files
seaweedfs/weed/storage/blockvol/config_test.go
T
Ping QiuandClaude Opus 4.6 3557ae283f feat: Phase 10 CP10-3 -- NVMe/TCP Tier 1 optimizations, WAL admission control, benchmark platform
CP10-3 Tier 1 optimizations (T1-T4):
- TCP_NODELAY + 256KB socket buffers on NVMe/TCP connections
- Response batching: all C2H data chunks + CapsuleResp in single flush
- Tiered buffer pool (4KB/64KB/256KB sync.Pool) for write payloads
- Configurable MaxH2CDataLength wiring through controller/IC/chunking

BUG-CP103-1: NVMe write retry with jittered backoff for transient WAL pressure
- writeWithRetry() with bounded backoff [50/200/800ms]
- throttleOnWALPressure() pre-write delay above 90% WAL usage
- WALPressureProvider interface + NVMeAdapter.WALPressure()

BUG-CP103-2: Volume-level WAL admission control
- WALAdmission with counting semaphore (max concurrent writers)
- Soft watermark (0.7): small delay to desynchronize herd
- Hard watermark (0.9): block until flusher drains
- Single-deadline budget shared across watermark wait + semaphore
- Close-aware during both watermark and semaphore waits
- Wired into BlockVol.WriteLBA() and Trim()

Benchmark platform enhancements:
- NVMe benchmark actions and scenarios (A/B, CW sweep, IOQ sweep)
- Database benchmark actions (SQLite, pgbench)
- K8s operator QA reconciler tests
- New testrunner scenarios for HA, fault injection, CSI lifecycle

Test counts: 213 NVMe + 625 engine + operator + testrunner tests, all passing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 17:44:01 -07:00

122 lines
3.3 KiB
Go

package blockvol
import (
"errors"
"testing"
"time"
)
func TestBlockVolConfig(t *testing.T) {
tests := []struct {
name string
run func(t *testing.T)
}{
{name: "config_defaults", run: testConfigDefaults},
{name: "config_validate_good", run: testConfigValidateGood},
{name: "config_validate_bad_shards", run: testConfigValidateBadShards},
{name: "config_validate_bad_threshold", run: testConfigValidateBadThreshold},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tt.run(t)
})
}
}
func testConfigDefaults(t *testing.T) {
cfg := DefaultConfig()
if cfg.GroupCommitMaxDelay != 1*time.Millisecond {
t.Errorf("GroupCommitMaxDelay = %v, want 1ms", cfg.GroupCommitMaxDelay)
}
if cfg.GroupCommitMaxBatch != 64 {
t.Errorf("GroupCommitMaxBatch = %d, want 64", cfg.GroupCommitMaxBatch)
}
if cfg.GroupCommitLowWatermark != 4 {
t.Errorf("GroupCommitLowWatermark = %d, want 4", cfg.GroupCommitLowWatermark)
}
if cfg.WALPressureThreshold != 0.8 {
t.Errorf("WALPressureThreshold = %f, want 0.8", cfg.WALPressureThreshold)
}
if cfg.WALFullTimeout != 5*time.Second {
t.Errorf("WALFullTimeout = %v, want 5s", cfg.WALFullTimeout)
}
if cfg.FlushInterval != 100*time.Millisecond {
t.Errorf("FlushInterval = %v, want 100ms", cfg.FlushInterval)
}
if cfg.DirtyMapShards != 256 {
t.Errorf("DirtyMapShards = %d, want 256", cfg.DirtyMapShards)
}
if err := cfg.Validate(); err != nil {
t.Errorf("DefaultConfig().Validate() = %v, want nil", err)
}
}
func testConfigValidateGood(t *testing.T) {
cases := []BlockVolConfig{
DefaultConfig(),
{
GroupCommitMaxDelay: 5 * time.Millisecond,
GroupCommitMaxBatch: 128,
GroupCommitLowWatermark: 0,
WALPressureThreshold: 1.0,
WALFullTimeout: 10 * time.Second,
FlushInterval: 50 * time.Millisecond,
DirtyMapShards: 1,
WALSoftWatermark: 0.5,
WALHardWatermark: 0.8,
WALMaxConcurrentWrites: 32,
},
{
GroupCommitMaxDelay: 1 * time.Microsecond,
GroupCommitMaxBatch: 1,
GroupCommitLowWatermark: 100,
WALPressureThreshold: 0.01,
WALFullTimeout: 1 * time.Millisecond,
FlushInterval: 1 * time.Millisecond,
DirtyMapShards: 1024,
WALSoftWatermark: 0.3,
WALHardWatermark: 0.6,
WALMaxConcurrentWrites: 4,
},
}
for i, cfg := range cases {
if err := cfg.Validate(); err != nil {
t.Errorf("case %d: Validate() = %v, want nil", i, err)
}
}
}
func testConfigValidateBadShards(t *testing.T) {
cases := []int{0, 3, 5, 7, 10, 15, -1}
for _, shards := range cases {
cfg := DefaultConfig()
cfg.DirtyMapShards = shards
err := cfg.Validate()
if err == nil {
t.Errorf("DirtyMapShards=%d: expected error, got nil", shards)
continue
}
if !errors.Is(err, errInvalidConfig) {
t.Errorf("DirtyMapShards=%d: expected errInvalidConfig, got %v", shards, err)
}
}
}
func testConfigValidateBadThreshold(t *testing.T) {
cases := []float64{0, -0.1, -1, 1.01, 2.0}
for _, thresh := range cases {
cfg := DefaultConfig()
cfg.WALPressureThreshold = thresh
err := cfg.Validate()
if err == nil {
t.Errorf("WALPressureThreshold=%f: expected error, got nil", thresh)
continue
}
if !errors.Is(err, errInvalidConfig) {
t.Errorf("WALPressureThreshold=%f: expected errInvalidConfig, got %v", thresh, err)
}
}
}