Files
seaweedfs/weed/storage/blockvol/testrunner/actions/fault.go
T
pingqiuandClaude Opus 4.6 785a7d7efd feat: wire real pinner into flusher retention + real WAL scan executor (Phase 07 P1)
Pinner wired to real retention:
- NewPinner calls vol.SetV2RetentionFloor(p.MinWALRetentionFloor)
- Flusher.RetentionFloorFn() / SetRetentionFloorFn() exposed
- SetV2RetentionFloor chains with existing shipper retention floor
- Holds actually prevent WAL reclaim (not just tracked state)

Executor uses real WAL scan:
- BlockVol.ScanWALEntries(fromLSN, callback) wraps wal.ScanFrom
  with real fd, walOffset, checkpointLSN
- Executor.StreamWALEntries uses ScanWALEntries (not stub)
- Reads real WAL entries, tracks highest LSN scanned

CommittedLSN mapping:
- Explicitly documented as interim V1 model (committed = checkpointed)
- Will diverge when V2 distributed commit separates from local flush

Carry-forward:
- TransferSnapshot/TransferFullBase/TruncateWAL: stubs (need extent I/O)
- Control intent from confirmed failover: deferred

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 20:01:46 -07:00

135 lines
3.6 KiB
Go

package actions
import (
"context"
"fmt"
tr "github.com/seaweedfs/seaweedfs/weed/storage/blockvol/testrunner"
"github.com/seaweedfs/seaweedfs/weed/storage/blockvol/testrunner/infra"
)
// RegisterFaultActions registers fault injection actions.
func RegisterFaultActions(r *tr.Registry) {
r.RegisterFunc("inject_netem", tr.TierChaos, injectNetemAction)
r.RegisterFunc("inject_partition", tr.TierChaos, injectPartitionAction)
r.RegisterFunc("fill_disk", tr.TierChaos, fillDiskAction)
r.RegisterFunc("corrupt_wal", tr.TierChaos, corruptWALAction)
r.RegisterFunc("clear_fault", tr.TierChaos, clearFaultAction)
}
func injectNetemAction(ctx context.Context, actx *tr.ActionContext, act tr.Action) (map[string]string, error) {
node, err := GetNode(actx, act.Node)
if err != nil {
return nil, fmt.Errorf("inject_netem: %w", err)
}
targetIP := act.Params["target_ip"]
if targetIP == "" {
return nil, fmt.Errorf("inject_netem: target_ip param required")
}
delayMs := ParseInt(act.Params["delay_ms"], 200)
cleanupCmd, err := infra.InjectNetem(ctx, node, targetIP, delayMs)
if err != nil {
return nil, err
}
// Store cleanup cmd for later clear_fault.
varKey := "__cleanup_netem"
if act.Node != "" {
varKey += "_" + act.Node
}
return map[string]string{varKey: cleanupCmd}, nil
}
func injectPartitionAction(ctx context.Context, actx *tr.ActionContext, act tr.Action) (map[string]string, error) {
node, err := GetNode(actx, act.Node)
if err != nil {
return nil, fmt.Errorf("inject_partition: %w", err)
}
targetIP := act.Params["target_ip"]
if targetIP == "" {
return nil, fmt.Errorf("inject_partition: target_ip param required")
}
ports := ParseIntSlice(act.Params["ports"])
if len(ports) == 0 {
return nil, fmt.Errorf("inject_partition: ports param required")
}
cleanupCmd, err := infra.InjectIptablesDrop(ctx, node, targetIP, ports)
if err != nil {
return nil, err
}
varKey := "__cleanup_partition"
if act.Node != "" {
varKey += "_" + act.Node
}
return map[string]string{varKey: cleanupCmd}, nil
}
func fillDiskAction(ctx context.Context, actx *tr.ActionContext, act tr.Action) (map[string]string, error) {
node, err := GetNode(actx, act.Node)
if err != nil {
return nil, fmt.Errorf("fill_disk: %w", err)
}
dir := act.Params["dir"]
if dir == "" {
dir = "/tmp"
}
cleanupCmd, err := infra.FillDisk(ctx, node, dir)
if err != nil {
return nil, err
}
varKey := "__cleanup_fill_disk"
if act.Node != "" {
varKey += "_" + act.Node
}
return map[string]string{varKey: cleanupCmd}, nil
}
func corruptWALAction(ctx context.Context, actx *tr.ActionContext, act tr.Action) (map[string]string, error) {
targetName := act.Target
if targetName == "" {
return nil, fmt.Errorf("corrupt_wal: target param required")
}
tgt, err := getHATarget(actx, targetName)
if err != nil {
return nil, err
}
nBytes := ParseInt(act.Params["bytes"], 4096)
return nil, infra.CorruptWALRegion(ctx, tgt.Node, tgt.VolFilePath(), nBytes)
}
func clearFaultAction(ctx context.Context, actx *tr.ActionContext, act tr.Action) (map[string]string, error) {
faultType := act.Params["type"]
if faultType == "" {
return nil, fmt.Errorf("clear_fault: type param required (netem, partition, fill_disk)")
}
node, err := GetNode(actx, act.Node)
if err != nil {
return nil, fmt.Errorf("clear_fault: %w", err)
}
varKey := "__cleanup_" + faultType
if act.Node != "" {
varKey += "_" + act.Node
}
cleanupCmd := actx.Vars[varKey]
if cleanupCmd == "" {
// Try without node suffix.
cleanupCmd = actx.Vars["__cleanup_"+faultType]
}
return nil, infra.ClearFault(ctx, node, cleanupCmd)
}