Files
seaweedfs/weed/storage/blockvol/testrunner/scenarios/internal/benchmark-pgbench.yaml
T
pingqiuandClaude Opus 4.6 ebe95b6e2e fix: flusher OOM on multi-block writes + testrunner enhancements
Bug: flusher.go:336 allocated make([]byte, entryLen) per dirty block
instead of per unique WAL entry. A 4MB WriteLBA creates 1024 dirty map
entries (one per 4KB block), all sharing the same WAL offset. The flusher
read the full 4MB WAL entry 1024 times into separate buffers:
1024 × 4MB = 4GB per 4MB write → OOM on mkfs.ext4.

Root cause: flusher assumed 1:1 dirty-block-to-WAL-entry mapping.
WriteLBA supports multi-block writes but the flusher never deduplicated
shared WAL offsets.

Fix: deduplicate WAL reads by WalOffset in flushOnceLocked(). Multiple
dirty blocks from the same WAL entry share one read buffer and one
DecodeWALEntry call. Memory: O(WAL_entries × size) not O(blocks × size).
For a 4MB write: 4GB → 4MB.

Verified on hardware (m01/M02 25Gbps RoCE):
- Before: mkfs.ext4 → VS RSS 100MB→25GB → OOM killed
- After: mkfs.ext4 → VS RSS 129MB stable, mkfs succeeds
- pgbench TPC-B c=4: 1,248 TPS (RF=1, previously blocked by OOM)

Tests added:
- flusher_test.go: flush_multiblock_shared_wal_read (16 blocks share
  one WAL offset, flush dedup verified)
- flusher_test.go: flush_multiblock_data_correct (3 mixed multi-block
  writes, all data correct after flush)
- test/component/large_write_test.go: 7 component tests (single 4MB,
  sequential mkfs sim, concurrent, mixed sizes, production volume,
  flusher throughput 30s sustained)
- iscsi/large_write_mem_test.go: 2 iSCSI session memory tests (4MB
  R2T flow, slow device)

Testrunner enhancements (same commit — all tested on hardware):
- discover_primary action: maps primary IP → topology node name,
  supports alt_ips for multi-NIC (RoCE + management)
- NodeSpec.AltIPs field for multi-NIC node identification
- 5 new YAML scenarios: ec3, ec5, degraded sync_all/best_effort, pgbench
- All 13 hardware-verified scenarios PASS

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 14:24:10 -07:00

210 lines
5.1 KiB
YAML

name: benchmark-pgbench
timeout: 15m
# Database benchmark: PostgreSQL pgbench on sw-block.
#
# Measures TPS for TPC-B (mixed read/write) and SELECT-only at various
# client counts. This is the most product-relevant benchmark — operators
# care about database performance on block storage.
#
# Competitive reference (March 2024, RF=2 sync, 25Gbps):
# Ceph RBD: 431 TPS
# Longhorn V1: 2,754 TPS
# sw-block: 3,226 TPS (old code, commit e92263b)
# Mayastor: 5,801 TPS
#
# Current sw-block number after syncWithWALProgress fix: unknown.
env:
master_url: "http://10.0.0.3:9433"
volume_name: bench-pgbench
# 2GB is enough for pgbench scale=10 (~150MB data).
# 4GB caused OOM on VS (20GB RSS → OOM killed).
vol_size: "2147483648"
topology:
nodes:
m01:
host: 192.168.1.181
alt_ips: ["10.0.0.1"]
user: testdev
key: "/opt/work/testdev_key"
m02:
host: 192.168.1.184
alt_ips: ["10.0.0.3"]
user: testdev
key: "/opt/work/testdev_key"
phases:
- name: cluster-start
actions:
- action: exec
node: m02
cmd: "fuser -k 9433/tcp 18480/tcp 2>/dev/null; sleep 1; rm -rf /tmp/sw-pgb-master /tmp/sw-pgb-vs1; mkdir -p /tmp/sw-pgb-master /tmp/sw-pgb-vs1/blocks"
root: "true"
ignore_error: true
- action: exec
node: m01
cmd: "fuser -k 18480/tcp 2>/dev/null; sleep 1; rm -rf /tmp/sw-pgb-vs2; mkdir -p /tmp/sw-pgb-vs2/blocks"
root: "true"
ignore_error: true
- action: start_weed_master
node: m02
port: "9433"
dir: /tmp/sw-pgb-master
extra_args: "-ip=10.0.0.3"
save_as: master_pid
- action: sleep
duration: 3s
- action: start_weed_volume
node: m02
port: "18480"
master: "10.0.0.3:9433"
dir: /tmp/sw-pgb-vs1
extra_args: "-block.dir=/tmp/sw-pgb-vs1/blocks -block.listen=:3295 -ip=10.0.0.3"
save_as: vs1_pid
- action: start_weed_volume
node: m01
port: "18480"
master: "10.0.0.3:9433"
dir: /tmp/sw-pgb-vs2
extra_args: "-block.dir=/tmp/sw-pgb-vs2/blocks -block.listen=:3295 -ip=10.0.0.1"
save_as: vs2_pid
- action: sleep
duration: 3s
- action: wait_cluster_ready
node: m02
master_url: "{{ master_url }}"
- action: wait_block_servers
count: "2"
- name: create-volume
actions:
- action: create_block_volume
name: "{{ volume_name }}"
size_bytes: "{{ vol_size }}"
replica_factor: "2"
durability_mode: "sync_all"
- action: wait_volume_healthy
name: "{{ volume_name }}"
timeout: 60s
- name: connect
actions:
- action: lookup_block_volume
name: "{{ volume_name }}"
save_as: vol
- action: iscsi_login_direct
node: m01
host: "{{ vol_iscsi_host }}"
port: "{{ vol_iscsi_port }}"
iqn: "{{ vol_iqn }}"
save_as: device
# Short sleep for device to settle.
- action: sleep
duration: 3s
- name: pgbench-setup
actions:
- action: pgbench_init
node: m01
device: "{{ device }}"
mount: "/mnt/pgbench"
port: "5434"
scale: "10"
fstype: "ext4"
- name: tpcb-c1
actions:
- action: pgbench_run
node: m01
clients: "1"
duration: "30"
save_as: tps_c1
- action: print
msg: "TPC-B c=1: {{ tps_c1 }} TPS"
- name: tpcb-c4
actions:
- action: pgbench_run
node: m01
clients: "4"
duration: "30"
save_as: tps_c4
- action: print
msg: "TPC-B c=4: {{ tps_c4 }} TPS"
- name: tpcb-c8
actions:
- action: pgbench_run
node: m01
clients: "8"
duration: "30"
save_as: tps_c8
- action: print
msg: "TPC-B c=8: {{ tps_c8 }} TPS"
- name: select-c4
actions:
- action: pgbench_run
node: m01
clients: "4"
duration: "30"
select_only: "true"
save_as: tps_sel
- action: print
msg: "SELECT-only c=4: {{ tps_sel }} TPS"
- name: results
actions:
- action: print
msg: "=== pgbench: RF=2 sync_all iSCSI/RoCE ==="
- action: print
msg: "TPC-B c=1: {{ tps_c1 }} TPS"
- action: print
msg: "TPC-B c=4: {{ tps_c4 }} TPS"
- action: print
msg: "TPC-B c=8: {{ tps_c8 }} TPS"
- action: print
msg: "SELECT-only c=4: {{ tps_sel }} TPS"
- action: collect_results
title: "pgbench: RF=2 sync_all iSCSI/RoCE"
volume_name: "{{ volume_name }}"
- name: cleanup
always: true
actions:
- action: pgbench_cleanup
node: m01
ignore_error: true
- action: iscsi_cleanup
node: m01
ignore_error: true
- action: stop_weed
node: m01
pid: "{{ vs2_pid }}"
ignore_error: true
- action: stop_weed
node: m02
pid: "{{ vs1_pid }}"
ignore_error: true
- action: stop_weed
node: m02
pid: "{{ master_pid }}"
ignore_error: true