mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-10-29 16:30:44 +01:00
Phase 3 delivers five checkpoints: CP1 Engine Tuning: BlockVolConfig tunables, 256-shard DirtyMap, adaptive group commit (low-watermark immediate flush), WAL pressure handling with backpressure and ErrWALFull timeout. CP2 iSCSI Session Refactor: RX/TX goroutine split with respCh (cap 64), txLoop for serialized response writes, StatSN assignment modes. Login phase stays single-goroutine; full-duplex after login. CP3 Store Integration: BlockVolAdapter (iscsi.BlockDevice interface), BlockVolumeStore management, BlockService in volume_server_block.go, CLI flags (--block.listen/dir/iqn.prefix), sw-block-attach.sh helper. CP5 Concurrency Hardening: WAL reuse guard (LSN validation in ReadLBA), opsOutstanding counter with beginOp/endOp + Close drain, appendWithRetry shared by WriteLBA and TrimLBA, flusher LSN guard in FlushOnce. Bug fixes (P3-BUG-2–11): unbounded pending queue cap, Data-Out timeout, flusher error logging, GroupCommitter panic recovery, Close vs concurrent ops guard, target shutdown race, WAL-full retry vs Close, WRITE SAME(16) for XFS, MODE SENSE(10) + VPD 0xB0/0xB2 for Linux kernel compatibility. 797 tests passing (517 engine + 280 iSCSI), go vet clean. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
116 lines
3.1 KiB
Go
116 lines
3.1 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,
|
|
},
|
|
{
|
|
GroupCommitMaxDelay: 1 * time.Microsecond,
|
|
GroupCommitMaxBatch: 1,
|
|
GroupCommitLowWatermark: 100,
|
|
WALPressureThreshold: 0.01,
|
|
WALFullTimeout: 1 * time.Millisecond,
|
|
FlushInterval: 1 * time.Millisecond,
|
|
DirtyMapShards: 1024,
|
|
},
|
|
}
|
|
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)
|
|
}
|
|
}
|
|
}
|