mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-10-20 13:04:25 +02: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>
110 lines
2.2 KiB
Go
110 lines
2.2 KiB
Go
package storage
|
|
|
|
import (
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"github.com/seaweedfs/seaweedfs/weed/storage/blockvol"
|
|
)
|
|
|
|
func createTestBlockVol(t *testing.T, dir, name string) string {
|
|
t.Helper()
|
|
path := filepath.Join(dir, name)
|
|
vol, err := blockvol.CreateBlockVol(path, blockvol.CreateOptions{
|
|
VolumeSize: 1024 * 4096,
|
|
BlockSize: 4096,
|
|
ExtentSize: 65536,
|
|
WALSize: 1 << 20,
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
vol.Close()
|
|
return path
|
|
}
|
|
|
|
func TestStoreAddBlockVolume(t *testing.T) {
|
|
dir := t.TempDir()
|
|
path := createTestBlockVol(t, dir, "vol1.blk")
|
|
|
|
bs := NewBlockVolumeStore()
|
|
defer bs.Close()
|
|
|
|
vol, err := bs.AddBlockVolume(path)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if vol == nil {
|
|
t.Fatal("expected non-nil volume")
|
|
}
|
|
|
|
// Duplicate add should fail.
|
|
_, err = bs.AddBlockVolume(path)
|
|
if err == nil {
|
|
t.Fatal("expected error on duplicate add")
|
|
}
|
|
|
|
// Should be listed.
|
|
paths := bs.ListBlockVolumes()
|
|
if len(paths) != 1 || paths[0] != path {
|
|
t.Fatalf("ListBlockVolumes: got %v", paths)
|
|
}
|
|
|
|
// Should be gettable.
|
|
got, ok := bs.GetBlockVolume(path)
|
|
if !ok || got != vol {
|
|
t.Fatal("GetBlockVolume failed")
|
|
}
|
|
}
|
|
|
|
func TestStoreRemoveBlockVolume(t *testing.T) {
|
|
dir := t.TempDir()
|
|
path := createTestBlockVol(t, dir, "vol1.blk")
|
|
|
|
bs := NewBlockVolumeStore()
|
|
defer bs.Close()
|
|
|
|
if _, err := bs.AddBlockVolume(path); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if err := bs.RemoveBlockVolume(path); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Should be gone.
|
|
if _, ok := bs.GetBlockVolume(path); ok {
|
|
t.Fatal("volume should have been removed")
|
|
}
|
|
|
|
// Remove again should fail.
|
|
if err := bs.RemoveBlockVolume(path); err == nil {
|
|
t.Fatal("expected error on double remove")
|
|
}
|
|
}
|
|
|
|
func TestStoreCloseAllBlockVolumes(t *testing.T) {
|
|
dir := t.TempDir()
|
|
path1 := createTestBlockVol(t, dir, "vol1.blk")
|
|
path2 := createTestBlockVol(t, dir, "vol2.blk")
|
|
|
|
bs := NewBlockVolumeStore()
|
|
|
|
if _, err := bs.AddBlockVolume(path1); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := bs.AddBlockVolume(path2); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if len(bs.ListBlockVolumes()) != 2 {
|
|
t.Fatalf("expected 2 volumes, got %d", len(bs.ListBlockVolumes()))
|
|
}
|
|
|
|
bs.Close()
|
|
|
|
if len(bs.ListBlockVolumes()) != 0 {
|
|
t.Fatalf("expected 0 volumes after close, got %d", len(bs.ListBlockVolumes()))
|
|
}
|
|
}
|