Files
seaweedfs/weed/storage/blockvol/testrunner/scenarios/internal/ha-multi-client-failover.yaml
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

163 lines
4.0 KiB
YAML

# F8: Multi-Client Concurrent Failover
#
# Tests: 2 iSCSI clients (different IQNs) connected to same primary.
# Both write data concurrently. Kill primary → promote replica →
# both clients reconnect to new primary → verify both datasets intact.
#
# Gap: Previously untested (session-storm is sequential, not concurrent failover)
#
# Note: Uses 2 clients (not 4) since both test nodes are available.
# client_node uses 2 separate iSCSI sessions with different initiator names.
#
# Pass criteria:
# - Both clients write successfully before failover
# - After failover, both datasets are intact on promoted replica
# - No data corruption or cross-client interference
name: ha-multi-client-failover
timeout: 5m
env:
repo_dir: "C:/work/seaweedfs"
topology:
nodes:
target_node:
host: "192.168.1.184"
user: testdev
key: "C:/work/dev_server/testdev_key"
client_node:
host: "192.168.1.181"
user: testdev
key: "C:/work/dev_server/testdev_key"
targets:
primary:
node: target_node
vol_size: 100M
iscsi_port: 3260
admin_port: 8080
iqn_suffix: f8-mc-primary
replica:
node: target_node
vol_size: 100M
iscsi_port: 3261
admin_port: 8081
replica_data_port: 9011
replica_ctrl_port: 9012
rebuild_port: 9013
iqn_suffix: f8-mc-replica
phases:
- name: setup
actions:
- action: kill_stale
node: target_node
ignore_error: true
- action: kill_stale
node: client_node
iscsi_cleanup: "true"
ignore_error: true
- action: build_deploy
- action: start_target
target: primary
create: "true"
- action: start_target
target: replica
create: "true"
- action: assign
target: replica
epoch: "1"
role: replica
lease_ttl: 60s
- action: assign
target: primary
epoch: "1"
role: primary
lease_ttl: 60s
- action: set_replica
target: primary
replica: replica
- name: client1_write
actions:
- action: iscsi_login
target: primary
node: client_node
save_as: device1
# Client 1 writes at offset 0.
- action: dd_write
node: client_node
device: "{{ device1 }}"
bs: 1M
count: "5"
save_as: md5_client1
- name: client1_write_offset
actions:
# Client 1 also writes at offset 10M (non-overlapping region for client2).
- action: dd_write
node: client_node
device: "{{ device1 }}"
bs: 1M
count: "5"
seek: "10"
save_as: md5_client2
- action: wait_lsn
target: replica
min_lsn: "2"
timeout: 10s
- name: failover
actions:
- action: iscsi_cleanup
node: client_node
ignore_error: true
- action: kill_target
target: primary
- action: assign
target: replica
epoch: "2"
role: primary
lease_ttl: 60s
- action: wait_role
target: replica
role: primary
timeout: 5s
- name: verify_both_datasets
actions:
- action: iscsi_login
target: replica
node: client_node
save_as: device2
# Verify client 1 data (offset 0).
- action: dd_read_md5
node: client_node
device: "{{ device2 }}"
bs: 1M
count: "5"
save_as: read_client1
- action: assert_equal
actual: "{{ read_client1 }}"
expected: "{{ md5_client1 }}"
# Verify client 2 data (offset 10M).
- action: dd_read_md5
node: client_node
device: "{{ device2 }}"
bs: 1M
count: "5"
skip: "10"
save_as: read_client2
- action: assert_equal
actual: "{{ read_client2 }}"
expected: "{{ md5_client2 }}"
- name: cleanup
always: true
actions:
- action: iscsi_cleanup
node: client_node
ignore_error: true
- action: stop_all_targets
ignore_error: true