mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-09-26 01:44:48 +00:00
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>
122 lines
3.3 KiB
Go
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)
|
|
}
|
|
}
|
|
}
|