mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-08-22 07:06:51 +00:00
Stage 0 (bootstrap closure): PASS on m01/M02
- create RF=2 sync_all → 10s shipper wait → 4k fsync → publish_healthy
- Proves: BarrierAccepted observation, ShipperConnected, DurableLSN > 0
Stage 1 (sustained workload): 32/33 actions PASS
- bootstrap → fio 10s randwrite → dd_write 1M×2 fsync → data checksum
- Remaining: auto-failover promotion (separate issue)
Key fixes:
- BarrierAccepted callback: SyncCache success → core DurableLSN update
- BarrierRejected callback: barrier failures surface to core with reason
- Shipper state callback for new volumes (not just startup volumes)
- CatchUpTo ctrl conn reset: prevents stale control channel after recovery
- CP13-6 max-bytes budget suspended: uses replicaFlushedLSN which can't
advance without barrier; kills healthy shippers during async writes.
Will be replaced by v2 negotiated sync/recovery protocol.
- Barrier diagnostic logging: start/fail/success with reason and LSN
- Scenario restructured: Stage 0 (bootstrap-closure) + Stage 1 (failover)
- dd_write: sync_mode param + real stderr capture
- sw-test-runner suite command: deploy once, run N scenarios
- WAL size plumbing: proto + API + handler (forward-compatible)
Known: 6 blockvol/server test failures from Barrier() path change
(bounded catch-up in Barrier). Need test updates to match new semantics.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
108 lines
2.9 KiB
Go
108 lines
2.9 KiB
Go
package blockapi
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
)
|
|
|
|
func TestClientCreateVolume(t *testing.T) {
|
|
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != "POST" || r.URL.Path != "/block/volume" {
|
|
t.Errorf("unexpected request: %s %s", r.Method, r.URL.Path)
|
|
w.WriteHeader(http.StatusNotFound)
|
|
return
|
|
}
|
|
var req CreateVolumeRequest
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
t.Errorf("decode request: %v", err)
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
return
|
|
}
|
|
if req.Name != "test-vol" {
|
|
t.Errorf("expected name test-vol, got %s", req.Name)
|
|
}
|
|
if req.WALSizeBytes != 256<<20 {
|
|
t.Errorf("expected wal_size_bytes %d, got %d", 256<<20, req.WALSizeBytes)
|
|
}
|
|
w.Header().Set("Content-Type", "application/json")
|
|
json.NewEncoder(w).Encode(VolumeInfo{
|
|
Name: req.Name,
|
|
VolumeServer: "vs1:9333",
|
|
SizeBytes: req.SizeBytes,
|
|
Epoch: 1,
|
|
Role: "primary",
|
|
Status: "active",
|
|
})
|
|
}))
|
|
defer ts.Close()
|
|
|
|
client := NewClient(ts.URL)
|
|
info, err := client.CreateVolume(context.Background(), CreateVolumeRequest{
|
|
Name: "test-vol",
|
|
SizeBytes: 1 << 30,
|
|
WALSizeBytes: 256 << 20,
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if info.Name != "test-vol" {
|
|
t.Errorf("expected name test-vol, got %s", info.Name)
|
|
}
|
|
if info.VolumeServer != "vs1:9333" {
|
|
t.Errorf("expected vs1:9333, got %s", info.VolumeServer)
|
|
}
|
|
}
|
|
|
|
func TestClientListVolumes(t *testing.T) {
|
|
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != "GET" || r.URL.Path != "/block/volumes" {
|
|
w.WriteHeader(http.StatusNotFound)
|
|
return
|
|
}
|
|
w.Header().Set("Content-Type", "application/json")
|
|
json.NewEncoder(w).Encode([]VolumeInfo{
|
|
{Name: "alpha", VolumeServer: "vs1:9333"},
|
|
{Name: "beta", VolumeServer: "vs2:9333"},
|
|
})
|
|
}))
|
|
defer ts.Close()
|
|
|
|
client := NewClient(ts.URL)
|
|
vols, err := client.ListVolumes(context.Background())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(vols) != 2 {
|
|
t.Fatalf("expected 2 volumes, got %d", len(vols))
|
|
}
|
|
if vols[0].Name != "alpha" || vols[1].Name != "beta" {
|
|
t.Errorf("unexpected volumes: %+v", vols)
|
|
}
|
|
}
|
|
|
|
func TestClientMultiMasterFallback(t *testing.T) {
|
|
// First server immediately rejects connections (closed listener).
|
|
dead := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}))
|
|
deadURL := dead.URL
|
|
dead.Close() // close it so connections are refused
|
|
|
|
// Second server responds normally.
|
|
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
json.NewEncoder(w).Encode([]VolumeInfo{})
|
|
}))
|
|
defer ts.Close()
|
|
|
|
client := NewClient(deadURL + "," + ts.URL)
|
|
vols, err := client.ListVolumes(context.Background())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if vols == nil {
|
|
t.Error("expected non-nil result")
|
|
}
|
|
}
|