Files
seaweedfs/weed/storage/blockvol/testrunner/actions/helpers.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

126 lines
3.0 KiB
Go

package actions
import (
"fmt"
"strconv"
"strings"
"time"
tr "github.com/seaweedfs/seaweedfs/weed/storage/blockvol/testrunner"
"github.com/seaweedfs/seaweedfs/weed/storage/blockvol/testrunner/infra"
)
// getNode retrieves the infra.Node for the named node from the action context.
func GetNode(actx *tr.ActionContext, name string) (*infra.Node, error) {
if name == "" {
// Try to get the first available node.
for _, n := range actx.Nodes {
if nn, ok := n.(*infra.Node); ok {
return nn, nil
}
}
return nil, fmt.Errorf("no nodes available")
}
n, ok := actx.Nodes[name]
if !ok {
return nil, fmt.Errorf("node %q not found", name)
}
nn, ok := n.(*infra.Node)
if !ok {
return nil, fmt.Errorf("node %q is not an infra.Node", name)
}
return nn, nil
}
// getTargetNode retrieves the node associated with a target.
func GetTargetNode(actx *tr.ActionContext, targetName string) (*infra.Node, error) {
spec, ok := actx.Scenario.Targets[targetName]
if !ok {
return nil, fmt.Errorf("target %q not in scenario", targetName)
}
return GetNode(actx, spec.Node)
}
// getTargetHost returns the host address for a target's node.
func GetTargetHost(actx *tr.ActionContext, targetName string) (string, error) {
spec, ok := actx.Scenario.Targets[targetName]
if !ok {
return "", fmt.Errorf("target %q not in scenario", targetName)
}
nodeSpec, ok := actx.Scenario.Topology.Nodes[spec.Node]
if !ok {
return "", fmt.Errorf("node %q not in topology", spec.Node)
}
if nodeSpec.IsLocal {
return "127.0.0.1", nil
}
return nodeSpec.Host, nil
}
func ParseDuration(s string) (time.Duration, error) {
return time.ParseDuration(s)
}
func ParseDurationMs(s string) (uint32, error) {
d, err := time.ParseDuration(s)
if err != nil {
// Try parsing as plain number (milliseconds).
ms, err2 := strconv.ParseUint(s, 10, 32)
if err2 != nil {
return 0, err
}
return uint32(ms), nil
}
return uint32(d.Milliseconds()), nil
}
func ParseInt(s string, def int) int {
if s == "" {
return def
}
v, err := strconv.Atoi(s)
if err != nil {
return def
}
return v
}
// parseSizeBytes converts a human-readable size string (e.g. "50M", "1G", "104857600") to bytes.
func ParseSizeBytes(s string) (uint64, error) {
s = strings.TrimSpace(s)
if s == "" {
return 0, fmt.Errorf("empty size string")
}
upper := strings.ToUpper(s)
var multiplier uint64 = 1
switch {
case strings.HasSuffix(upper, "G"):
multiplier = 1024 * 1024 * 1024
s = strings.TrimSuffix(upper, "G")
case strings.HasSuffix(upper, "M"):
multiplier = 1024 * 1024
s = strings.TrimSuffix(upper, "M")
case strings.HasSuffix(upper, "K"):
multiplier = 1024
s = strings.TrimSuffix(upper, "K")
default:
s = upper
}
v, err := strconv.ParseUint(s, 10, 64)
if err != nil {
return 0, fmt.Errorf("parse size %q: %w", s, err)
}
return v * multiplier, nil
}
func ParseIntSlice(s string) []int {
var result []int
for _, part := range strings.Split(s, ",") {
part = strings.TrimSpace(part)
if v, err := strconv.Atoi(part); err == nil {
result = append(result, v)
}
}
return result
}