Files
seaweedfs/sw-block/design/testrunner-roadmap.md
T
pingqiuandClaude Opus 4.6 2bc8dfcdde doc: update testrunner roadmap — add runs.db text index for result tracking
P1 feature updated: replace generic "structured results" with concrete
runs.db design (newline-delimited JSON, one line per run). Leverages
existing RunBundle system (manifest.json, result.json already exist).

New CLI commands: list, trend, gc, reindex, diff.
Regression detection via stddev comparison against rolling baseline.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 22:00:35 -07:00

16 KiB

sw-test-runner Roadmap

Date: 2026-04-11 Status: active

1. Current State

The sw-test-runner is a YAML-driven test platform for sw-block hardware validation. It deploys binaries, orchestrates multi-node scenarios, runs benchmarks, injects faults, and reports results.

Metric Value
Registered actions 37
YAML scenarios (internal) 72
YAML scenarios (external) 75
Scenario categories smoke, HA, rebuild, chaos, perf, soak, CSI
Hardware tested m01/m02, 25Gbps RoCE

What it does well

  • Deploy weed binaries to remote nodes via SSH
  • Start/stop/kill weed processes (master, volume server)
  • Create/delete block volumes via master gRPC API
  • iSCSI and NVMe-oF connect/disconnect
  • fio benchmark with JSON output + metric parsing
  • dd write/read/verify with md5 checksum
  • Fault injection: netem delay, iptables partition, disk fill, WAL corrupt
  • Phase-based scenario sequencing with variable substitution
  • Assertions: equal, greater, contains, block field checks
  • Artifact collection on failure
  • JUnit XML output for CI
  • Parallel phase support
  • Prometheus metrics scraping

What it cannot do yet

  • Compare multiple binaries (V1/V2/V3) in one run
  • Compare against other systems (Ceph RBD, DRBD)
  • Track results over time (no persistent history)
  • Detect performance regressions automatically
  • Debug failures (auto-grep logs, stop-on-fail)
  • Deploy clusters from scratch (relies on pre-installed weed)
  • Reuse cluster setup across scenarios (boilerplate every time)

2. Proven Scenarios (V2 Baseline)

These 4 scenarios define the V2 acceptance bar. All PASS as of 2026-04-11.

Scenario File Actions What it proves
I-V3 Auto-Failover recovery-baseline-failover.yaml 43/43 Create→write→kill→promote→IO verified
I-R8 Rebuild-Rejoin v2-rebuild-rejoin.yaml 58/58 Failover→write→restart→1GB rebuild→data verified
Fast Rejoin v2-fast-rejoin-catchup.yaml 43/43 Kill replica→3s restart→recovery→data verified
RF=1 Perf Baseline rf1-perf-compare.yaml 22/22 V1.5 vs V2 IOPS comparison (within 1.2%)

Additional proven scenarios:

Scenario File Actions What it proves
Rebuild Retry v2-rebuild-failure-retry.yaml 52/52 Kill during rebuild→restart→data verified
dm-stripe 2-server dm-stripe-two-server.yaml 42/42 Linux striping across 2 BlockVols: 1.87x write IOPS

3. Roadmap

P0: Multi-Version Comparison Matrix

Goal: Run the same benchmark scenario against V1.5, V2, V3, and Ceph in one invocation. Produce a side-by-side comparison table.

Why P0: CEO needs head-to-head performance numbers for investor/customer presentations. Current workflow requires manual binary swaps and separate runs.

Design:

# New top-level field in scenario YAML:
matrix:
  binary:
    - name: v1.5
      weed: /opt/work/weed-v1.5
    - name: v2
      weed: /opt/work/weed
    - name: ceph
      type: ceph
      pool: bench-pool

# Runner executes the scenario once per matrix entry,
# collects metrics, produces comparison table.

Output:

=== perf-compare: 4K randwrite qd=32, RF=1, 15s ===

| Binary | Write IOPS | Write P99 | Read IOPS | Read P99 |
|--------|-----------|-----------|-----------|----------|
| v1.5   | 47,233    | 1,234 us  | 62,100    | 890 us   |
| v2     | 46,666    | 1,250 us  | 61,800    | 910 us   |
| ceph   | 4,533     | 14,483 us | 65,761    | 1,319 us |

New code:

Component Description Est. lines
Matrix parser Parse matrix: from YAML, generate run combinations 80
Matrix executor Run scenario N times, collect results per variant 120
Comparison reporter Markdown/terminal table from collected metrics 80
Total ~280

Acceptance: sw-test-runner compare rf1-perf-compare.yaml produces the table above with real numbers from hardware.


P0: Ceph RBD Adapter

Goal: Run fio benchmarks against Ceph RBD on the same hardware, using the same scenario YAML. Enable head-to-head comparison.

Why P0: Validates the 13.5x write IOPS advantage claim. Without this, performance claims are from separate, non-reproducible runs.

Prerequisites: Ceph cluster deployed on m01/m02 (one monitor, two OSDs). This is a one-time manual setup.

New actions:

Action Parameters What it does
ceph_create_image pool, name, size rbd create --size {size} {pool}/{name}
ceph_map pool, name, node, save_as rbd map {pool}/{name} → returns /dev/rbdN
ceph_unmap device, node rbd unmap {device}
ceph_delete_image pool, name rbd rm {pool}/{name}

Est. lines: ~100 (4 actions, each ~25 lines wrapping CLI commands)

Acceptance: ceph_create_image + ceph_map + fio_json + ceph_unmap works in a scenario YAML.


P1: Result Index + History (text-db)

Goal: Index all run bundles in a lightweight text database. Support listing, searching, comparing, and cleaning up old runs.

Why P1: Runs accumulate as directories on the test node. Without an index, you can't find old results, compare trends, or clean up disk space.

What already exists (RunBundle system):

results/
  20260409-175214-d099/           ← one dir per run (timestamp-based)
    manifest.json                 ← run identity: scenario, git SHA, binary hash
    scenario.yaml                 ← frozen copy of input YAML
    result.json                   ← full structured result (phases, actions, vars)
    result.xml                    ← JUnit XML
    result.html                   ← HTML report
    artifacts/                    ← collected logs on failure
  20260410-012330-5d99/
    ...

Each run is self-contained. manifest.json has: run_id, scenario_name, scenario_sha256, git_sha, host, status, command_line, started_at, finished_at.

What's missing: an index across runs.

Design: Add a runs.db text file (one JSON line per run) that acts as a lightweight index. No external database dependency.

results/
  runs.db                         ← newline-delimited JSON (one line per run)
  20260409-175214-d099/
    manifest.json
    result.json
    ...

runs.db format (one JSON line per entry):

{"run_id":"20260409-175214-d099","scenario":"v2-rebuild-rejoin","status":"pass","actions":58,"passed":58,"failed":0,"duration_ms":181795,"iops_write":46666,"iops_read":0,"git_sha":"8ecc50645","binary_md5":"4bcf08","started_at":"2026-04-09T17:52:14Z","dir":"20260409-175214-d099","disk_mb":12}
{"run_id":"20260410-012330-5d99","scenario":"recovery-baseline-failover","status":"pass","actions":43,"passed":43,"failed":0,"duration_ms":111583,"iops_write":28733,"git_sha":"8ecc50645","binary_md5":"4bcf08","started_at":"2026-04-10T01:23:30Z","dir":"20260410-012330-5d99","disk_mb":8}

New CLI commands:

# List all runs, most recent first
sw-test-runner list
  RUN_ID                  SCENARIO                    STATUS  ACTIONS  IOPS_W   DURATION
  20260411-140236-8012    dm-stripe-two-server        pass    42/42    79001    1m10s
  20260410-012330-5d99    recovery-baseline-failover   pass    43/43    28733    1m52s
  20260409-175214-d099    v2-rebuild-rejoin           pass    58/58    —        3m02s

# Filter by scenario
sw-test-runner list --scenario rebuild
sw-test-runner list --status fail
sw-test-runner list --since 2026-04-10

# Show trend for a scenario
sw-test-runner trend rf1-perf-compare
  DATE        GIT_SHA     IOPS_WRITE  IOPS_READ   STATUS
  2026-04-10  8ecc506     47233       —           pass (v1.5)
  2026-04-10  8ecc506     46666       —           pass (v2)

# Compare two runs
sw-test-runner diff 20260410-run1 20260410-run2

# Clean up old runs (keep last N per scenario)
sw-test-runner gc --keep 10
  Deleted 23 runs, freed 1.2 GB

# Rebuild index from existing run directories
sw-test-runner reindex
  Scanned 45 run directories, indexed 45 entries

New code:

Component Description Est. lines
Index writer Append one JSON line to runs.db after each run 50
Index reader Parse runs.db, filter/sort/search 80
list command Terminal table from index 60
trend command Filter by scenario, show metrics over time 60
gc command Delete old run dirs, update index 50
reindex command Scan run dirs, rebuild runs.db from manifest+result 60
Disk size calculator du -s each run dir, store in index 20
Total ~380

Regression detection:

sw-test-runner trend rf1-perf-compare --check
  Latest: 46,666 IOPS
  Baseline (last 10): 47,100 ± 800 IOPS
  Status: OK (within 1 stddev)

sw-test-runner trend rf1-perf-compare --check
  Latest: 38,000 IOPS
  Baseline (last 10): 47,100 ± 800 IOPS
  Status: REGRESSION (-19.3%, > 2 stddev)

Acceptance: After 5 runs, sw-test-runner list shows all 5 with metrics. sw-test-runner gc --keep 3 deletes the 2 oldest.


P1: Cluster Templates

Goal: Remove the 20-line cluster boilerplate from every scenario. Define reusable topology+cluster configs.

Why P1: Every new scenario copies the same cluster-start phase. Changes to cluster config (ports, dirs) require editing every scenario.

Design:

# templates/two-node-roce.yaml
cluster:
  master:
    node: m02
    port: 9433
    dir: /tmp/sw-master
    extra_args: "-ip=10.0.0.3"
  volumes:
    - node: m02
      port: 18480
      dir: /tmp/sw-vs1
      block_dir: /tmp/sw-vs1/blocks
      block_listen: ":3295"
      extra_args: "-ip=10.0.0.3"
    - node: m01
      port: 18480
      dir: /tmp/sw-vs2
      block_dir: /tmp/sw-vs2/blocks
      block_listen: ":3295"
      extra_args: "-ip=10.0.0.1"
# scenario references template:
name: rebuild-rejoin
cluster: two-node-roce       # ← replaces 20 lines of cluster-start
phases:
  - name: create-and-write
    ...

New code:

Component Description Est. lines
Template loader Parse cluster template YAML 60
Auto cluster-start phase Generate start actions from template 80
Auto cleanup phase Generate stop actions from template 40
Variable injection Expose {{ master_pid }}, {{ vs1_pid }}, etc. 30
Total ~210

Acceptance: v2-rebuild-rejoin.yaml works with cluster: two-node-roce instead of inline cluster-start phase.


P2: Debug Mode

Goal: When a scenario fails, automatically collect diagnostic information and optionally pause for manual investigation.

Why P2: This session's debugging workflow was: scenario fails → SSH to node → grep logs → find root cause → fix → redeploy → rerun. The runner should automate the grep+collect step.

New CLI flags:

sw-test-runner run scenario.yaml --debug          # auto-grep + stop on fail
sw-test-runner run scenario.yaml --stop-on-fail   # pause, don't cleanup
sw-test-runner run scenario.yaml --grep "pattern"  # grep all node logs

Auto-diagnostic on failure:

=== FAILURE DIAGNOSTIC ===
Phase: restart-old-primary
Action: wait_volume_healthy (timeout 60s)

--- m01 volume.log (last 20 lines matching "error|fail|warn|panic") ---
W0409 15:41:18 recovery: rebuild execution failed: sender not found
...

--- m02 volume.log (last 20 lines matching "error|fail|warn|panic") ---
(clean)

--- m02 master.log (last 10 lines matching "failover|promote|assign") ---
I0409 15:41:12 failover: promoted replica for "v2-rebuild"
...

New code:

Component Description Est. lines
--stop-on-fail handler Skip cleanup, print SSH commands 30
Auto-grep on failure SSH to all nodes, grep key patterns 80
Log merge by timestamp Merge multi-node logs, sort by time 60
Diagnostic formatter Terminal-friendly failure report 40
Total ~210

Acceptance: When v2-rebuild-rejoin.yaml fails, the runner automatically shows the "sender not found" error from the logs without manual SSH.


P2: RF=2 Performance Benchmark Suite

Goal: Standardized benchmark scenario that matches the V1 bench parameters exactly. Produces results comparable to the CEO's numbers.

Why P2: Our current V2 perf numbers are RF=1 only. The CEO's benchmark was RF=2 sync_all over NVMe/TCP. We need the same config.

Scenario:

name: rf2-perf-benchmark
cluster: two-node-roce

phases:
  - name: create-volume
    actions:
      - action: create_block_volume
        name: perf-vol
        size_bytes: "1073741824"
        replica_factor: "2"
        durability_mode: "sync_all"

  - name: benchmark
    actions:
      # Warmup
      - action: fio_json
        rw: randwrite
        bs: 4k
        iodepth: "32"
        runtime: "10"

      # 4K random write
      - action: fio_json
        rw: randwrite
        bs: 4k
        iodepth: "32"
        runtime: "15"
        save_as: write_result

      # 4K random read
      - action: fio_json
        rw: randread
        bs: 4k
        iodepth: "32"
        runtime: "15"
        save_as: read_result

      # 128K sequential write
      - action: fio_json
        rw: write
        bs: 128k
        iodepth: "16"
        runtime: "15"
        save_as: seq_write_result

      # Report
      - action: print
        msg: |
          RF=2 sync_all Performance:
          4K randwrite: {{ write_iops }} IOPS, P99={{ write_p99 }}us
          4K randread:  {{ read_iops }} IOPS, P99={{ read_p99 }}us
          128K seqwrite: {{ seq_bw }} MB/s

P3: Full Cluster Deployment

Goal: Deploy a complete sw-block cluster (master + N volume servers + CSI driver) from scratch on bare metal or K8s.

Why P3: Currently assumes pre-installed binaries and manual OS setup. For CI/CD and customer POC, need push-button deployment.

Scope:

Component What it does
OS prereqs Install iscsiadm, nvme-cli, fio, dmsetup
Binary deployment Build + SCP weed binary to all nodes
Cluster bootstrap Start master, wait ready, start volume servers
Volume provisioning Create volumes, configure replication
Client setup iSCSI/NVMe-oF discovery + login
Health check Verify all components healthy

Effort: ~500 lines. Depends on target environment (bare metal vs K8s).


P3: pgbench / Application Benchmark

Goal: Run real database benchmarks (pgbench TPC-B, sysbench OLTP) on sw-block volumes to produce application-level metrics.

Why P3: fio shows raw block performance. Customers care about "how fast is PostgreSQL on your storage."

New actions:

Action What it does
pgbench_init pgbench -i -s {scale} -h {host} {db}
pgbench_run pgbench -c {clients} -T {time} -h {host} {db} → parse TPS
start_postgres Start PostgreSQL on a block PV
stop_postgres Stop PostgreSQL cleanly

4. Implementation Priority

Priority Feature Lines Impact
P0 Multi-version comparison matrix 280 CEO benchmark table
P0 Ceph RBD adapter 100 Head-to-head validation
P1 Structured results + trends 300 Regression detection
P1 Cluster templates 210 Scenario authoring speed
P2 Debug mode 210 Debugging speed
P2 RF=2 perf benchmark suite 1 scenario V1 number comparison
P3 Full cluster deployment 500 CI/CD, customer POC
P3 pgbench / app benchmarks 200 Customer-facing metrics
Total ~1,800

5. Versioning Strategy

The testrunner should work across sw-block versions:

Version Binary Scenarios Expected behavior
V1.5 weed-v1.5 rf1-perf-compare only RF=1 perf baseline
V2 weed (current) All 147 scenarios Full coverage
V3 weed-v3 (future) Same 4 acceptance scenarios Must match V2 results
Ceph rbd CLI Perf scenarios via adapter Comparison baseline

The matrix runner handles this — same scenario, different binary, automatic comparison.

6. Non-Goals

The testrunner should NOT become:

  • A full CI/CD pipeline (use GitHub Actions / Jenkins for that)
  • A monitoring system (use Prometheus/Grafana for ongoing monitoring)
  • A configuration management tool (use Ansible/Terraform for cluster setup)
  • A log aggregation system (use ELK/Loki for production log analysis)

It IS: a focused hardware validation and benchmark tool for sw-block development and customer demos.