13 changed files with 601 additions and 15 deletions
@ -0,0 +1,339 @@
|
||||
#!/usr/bin/env python3 |
||||
"""Burst/load peer for tcp_proxy integration test (full-duplex, byte-exact). |
||||
|
||||
Both sides stream a known pseudo-random byte sequence in random chunks |
||||
(10B..1MB) with optional random idle (100..500ms) between chunks, over full-duplex |
||||
TCP connections, and verify the received stream byte-for-byte. |
||||
|
||||
Because the sequence is deterministic (self-contained xorshift64* generator), |
||||
every received byte is checked against a pre-known value — any corruption, |
||||
cross-connection mixing or stall is detected exactly. |
||||
|
||||
Multiple concurrent connections are supported (--connections N); each connection |
||||
carries its index in a 4-byte handshake so both peers derive distinct per-connection |
||||
seeds and any cross-connection byte mixing is caught. |
||||
|
||||
Roles: |
||||
server: burst_peer.py --server --host 127.0.0.1 --port 19193 --connections 8 |
||||
client: burst_peer.py --host 10.200.100.1 --port 19193 --mark 1 --connections 8 |
||||
|
||||
Exit code: 0 = PASS (all connections byte-exact), 1 = FAIL. |
||||
""" |
||||
|
||||
import argparse |
||||
import math |
||||
import random |
||||
import select |
||||
import signal |
||||
import socket |
||||
import struct |
||||
import sys |
||||
import threading |
||||
import time |
||||
|
||||
SO_MARK = 36 |
||||
MASK64 = (1 << 64) - 1 |
||||
|
||||
|
||||
class ByteStream: |
||||
"""Deterministic xorshift64* byte stream (version-independent).""" |
||||
|
||||
def __init__(self, seed): |
||||
self.s = (seed ^ 0x9E3779B97F4A7C15) & MASK64 |
||||
if self.s == 0: |
||||
self.s = 0x123456789ABCDEF |
||||
|
||||
def next_u64(self): |
||||
x = self.s |
||||
x ^= (x >> 12) & MASK64 |
||||
x ^= (x << 25) & MASK64 |
||||
x ^= (x >> 27) & MASK64 |
||||
self.s = x |
||||
return (x * 0x2545F4914F6CDD1D) & MASK64 |
||||
|
||||
def next_bytes(self, n): |
||||
out = bytearray() |
||||
while len(out) < n: |
||||
out += self.next_u64().to_bytes(8, "little") |
||||
return bytes(out[:n]) |
||||
|
||||
|
||||
def rand_chunk(rnd, lo, hi): |
||||
"""Log-uniform chunk size in [lo, hi] — covers small (keep-alive) and large.""" |
||||
if hi <= lo: |
||||
return lo |
||||
v = math.exp(rnd.uniform(math.log(lo), math.log(hi))) |
||||
return max(lo, min(hi, int(round(v)))) |
||||
|
||||
|
||||
class StuckError(Exception): |
||||
pass |
||||
|
||||
|
||||
def send_all(sock, data): |
||||
view = memoryview(data) |
||||
while len(view): |
||||
n = sock.send(view) |
||||
if n <= 0: |
||||
raise RuntimeError("send failed (connection closed)") |
||||
view = view[n:] |
||||
|
||||
|
||||
def recv_exact(sock, n): |
||||
out = bytearray() |
||||
while len(out) < n: |
||||
c = sock.recv(n - len(out)) |
||||
if not c: |
||||
raise RuntimeError("connection closed") |
||||
out += c |
||||
return bytes(out) |
||||
|
||||
|
||||
def receive_session(sock, recv_seed, total, stall_ms, name): |
||||
"""Read frames until END, verify byte-for-byte. Returns (bytes, frames).""" |
||||
rng = ByteStream(recv_seed) |
||||
received = 0 |
||||
nframes = 0 |
||||
last_progress = time.monotonic() |
||||
last_log = time.monotonic() |
||||
|
||||
def read_n(n): |
||||
nonlocal last_progress |
||||
out = bytearray() |
||||
while len(out) < n: |
||||
r, _, _ = select.select([sock], [], [], 0.1) |
||||
if r: |
||||
c = sock.recv(n - len(out)) |
||||
if not c: |
||||
raise RuntimeError("connection closed at %d/%d bytes" % (received, total)) |
||||
out += c |
||||
last_progress = time.monotonic() |
||||
else: |
||||
if time.monotonic() - last_progress >= stall_ms: |
||||
raise StuckError("no progress for %dms (received %d/%d)" % |
||||
(stall_ms, received, total)) |
||||
return bytes(out) |
||||
|
||||
while True: |
||||
hdr = read_n(4) |
||||
(length,) = struct.unpack(">I", hdr) |
||||
if length == 0: |
||||
if received != total: |
||||
raise RuntimeError("peer ended early: %d/%d" % (received, total)) |
||||
return received, nframes |
||||
if received + length > total: |
||||
raise RuntimeError("frame overflow: %d+%d > %d" % (received, length, total)) |
||||
payload = read_n(length) |
||||
expected = rng.next_bytes(length) |
||||
if payload != expected: |
||||
off = 0 |
||||
for i in range(length): |
||||
if payload[i] != expected[i]: |
||||
off = i |
||||
break |
||||
raise RuntimeError("byte mismatch at offset %d: got %02x want %02x" % |
||||
(received + off, payload[off], expected[off])) |
||||
received += length |
||||
nframes += 1 |
||||
if time.monotonic() - last_log >= 1.0: |
||||
print("[%s] recv %d/%d bytes" % (name, received, total), flush=True) |
||||
last_log = time.monotonic() |
||||
|
||||
|
||||
def send_session(sock, send_seed, total, min_chunk, max_chunk, min_idle, max_idle): |
||||
"""Send `total` deterministic bytes in random chunks with random idle, then END.""" |
||||
rng = ByteStream(send_seed) |
||||
rnd = random.Random(send_seed ^ 0xABCDEF) |
||||
remaining = total |
||||
nframes = 0 |
||||
while remaining > 0: |
||||
length = rand_chunk(rnd, min_chunk, max_chunk) |
||||
if length > remaining: |
||||
length = remaining |
||||
if length < 1: |
||||
length = 1 |
||||
payload = rng.next_bytes(length) |
||||
send_all(sock, struct.pack(">I", length) + payload) |
||||
remaining -= length |
||||
nframes += 1 |
||||
if remaining > 0: |
||||
time.sleep(rnd.uniform(min_idle, max_idle) / 1000.0) |
||||
send_all(sock, struct.pack(">I", 0)) |
||||
return nframes, total |
||||
|
||||
|
||||
def run_peer(sock, args, role, name, idx): |
||||
"""Run one full-duplex session. Returns (ok, recv_bytes, send_bytes, err).""" |
||||
if role == "client": |
||||
seed_out, seed_in = args.seed_c2s ^ idx, args.seed_s2c ^ idx |
||||
else: |
||||
seed_out, seed_in = args.seed_s2c ^ idx, args.seed_c2s ^ idx |
||||
|
||||
sender_res = {} |
||||
|
||||
def sender(): |
||||
try: |
||||
sender_res["frames"], sender_res["bytes"] = send_session( |
||||
sock, seed_out, args.total, |
||||
args.min_chunk, args.max_chunk, |
||||
args.min_idle_ms, args.max_idle_ms) |
||||
sender_res["ok"] = True |
||||
except Exception as e: |
||||
sender_res["ok"] = False |
||||
sender_res["err"] = str(e) |
||||
|
||||
t = threading.Thread(target=sender, daemon=True) |
||||
t.start() |
||||
|
||||
try: |
||||
recv, _ = receive_session(sock, seed_in, args.total, args.stall_ms, name) |
||||
except StuckError as e: |
||||
return False, 0, 0, "STUCK: %s" % e |
||||
except Exception as e: |
||||
return False, 0, 0, str(e) |
||||
|
||||
t.join() |
||||
if not sender_res.get("ok"): |
||||
return False, recv, 0, "send error: %s" % sender_res.get("err") |
||||
|
||||
return True, recv, sender_res["bytes"], None |
||||
|
||||
|
||||
def main(): |
||||
p = argparse.ArgumentParser() |
||||
p.add_argument("--server", action="store_true") |
||||
p.add_argument("--host", required=True) |
||||
p.add_argument("--port", type=int, required=True) |
||||
p.add_argument("--name", default="burst") |
||||
p.add_argument("--connections", type=int, default=1) |
||||
p.add_argument("--total", type=int, default=4 * 1024 * 1024) |
||||
p.add_argument("--min-chunk", type=int, default=10) |
||||
p.add_argument("--max-chunk", type=int, default=1048576) |
||||
p.add_argument("--min-idle-ms", type=int, default=100) |
||||
p.add_argument("--max-idle-ms", type=int, default=500) |
||||
p.add_argument("--stall-ms", type=int, default=2000) |
||||
p.add_argument("--mark", type=int, default=1) |
||||
p.add_argument("--seed-c2s", type=lambda x: int(x, 0), default=0x11443322778855A) |
||||
p.add_argument("--seed-s2c", type=lambda x: int(x, 0), default=0x2233445511223344) |
||||
args = p.parse_args() |
||||
|
||||
if args.server: |
||||
run_server(args) |
||||
else: |
||||
run_client(args) |
||||
|
||||
|
||||
def run_client(args): |
||||
results = [] |
||||
lock = threading.Lock() |
||||
t0 = time.monotonic() |
||||
|
||||
def worker(i): |
||||
name = "%s[%d]" % (args.name, i) |
||||
s = None |
||||
try: |
||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
||||
if args.mark: |
||||
s.setsockopt(socket.SOL_SOCKET, SO_MARK, args.mark) |
||||
s.connect((args.host, args.port)) |
||||
s.sendall(struct.pack(">I", i)) |
||||
ok, recv, send, err = run_peer(s, args, "client", name, i) |
||||
if ok: |
||||
print("[PASS] %s: recv %d sent %d" % (name, recv, send), flush=True) |
||||
else: |
||||
print("[FAIL] %s: %s" % (name, err), flush=True) |
||||
with lock: |
||||
results.append((ok, recv, send)) |
||||
except OSError as e: |
||||
print("[FAIL] %s: connect: %s" % (name, e), flush=True) |
||||
with lock: |
||||
results.append((False, 0, 0)) |
||||
finally: |
||||
if s: |
||||
s.close() |
||||
|
||||
threads = [threading.Thread(target=worker, args=(i,)) for i in range(args.connections)] |
||||
for t in threads: |
||||
t.start() |
||||
for t in threads: |
||||
t.join() |
||||
|
||||
dt = time.monotonic() - t0 |
||||
ok = len(results) == args.connections and all(r[0] for r in results) |
||||
total_bytes = sum(r[1] + r[2] for r in results) |
||||
if ok: |
||||
print("[PASS] %s: %d/%d connections, %d bytes in %.2fs (%.2f MB/s)" % |
||||
(args.name, len(results), args.connections, total_bytes, dt, |
||||
total_bytes / 1e6 / dt if dt > 0 else 0), flush=True) |
||||
sys.exit(0) |
||||
failed = args.connections - sum(1 for r in results if r[0]) |
||||
print("[FAIL] %s: %d/%d connections failed" % (args.name, failed, args.connections), flush=True) |
||||
sys.exit(1) |
||||
|
||||
|
||||
def run_server(args): |
||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
||||
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) |
||||
s.bind((args.host, args.port)) |
||||
s.listen(256) |
||||
print("BURST: listening on %s:%d connections=%d total=%d" % |
||||
(args.host, args.port, args.connections, args.total), flush=True) |
||||
|
||||
results = [] |
||||
lock = threading.Lock() |
||||
running = [True] |
||||
|
||||
def _stop(sig, frame): |
||||
running[0] = False |
||||
|
||||
signal.signal(signal.SIGTERM, _stop) |
||||
signal.signal(signal.SIGINT, _stop) |
||||
|
||||
def handle(conn): |
||||
idx = 0 |
||||
try: |
||||
idxb = recv_exact(conn, 4) |
||||
(idx,) = struct.unpack(">I", idxb) |
||||
name = "%s[%d]" % (args.name, idx) |
||||
ok, recv, send, err = run_peer(conn, args, "server", name, idx) |
||||
if ok: |
||||
print("[PASS] %s: recv %d sent %d" % (name, recv, send), flush=True) |
||||
else: |
||||
print("[FAIL] %s: %s" % (name, err), flush=True) |
||||
with lock: |
||||
results.append(ok) |
||||
except OSError as e: |
||||
print("[FAIL] %s: %s" % (args.name, e), flush=True) |
||||
with lock: |
||||
results.append(False) |
||||
finally: |
||||
conn.close() |
||||
|
||||
s.settimeout(1.0) |
||||
threads = [] |
||||
while running[0] and len(threads) < args.connections: |
||||
try: |
||||
conn, addr = s.accept() |
||||
except socket.timeout: |
||||
continue |
||||
except OSError: |
||||
break |
||||
t = threading.Thread(target=handle, args=(conn,), daemon=True) |
||||
t.start() |
||||
threads.append(t) |
||||
|
||||
for t in threads: |
||||
t.join() |
||||
s.close() |
||||
|
||||
ok = len(results) == args.connections and all(results) |
||||
if ok: |
||||
print("[PASS] %s: %d/%d connections" % (args.name, len(results), args.connections), flush=True) |
||||
sys.exit(0) |
||||
print("[FAIL] %s: %d/%d connections ok" % |
||||
(args.name, sum(1 for r in results if r), args.connections), flush=True) |
||||
sys.exit(1) |
||||
|
||||
|
||||
if __name__ == "__main__": |
||||
main() |
||||
@ -0,0 +1,168 @@
|
||||
#!/bin/bash |
||||
# run_burst_test.sh — burst-тест tcp_proxy: двусторонний byte-exact трафик со случайными idle |
||||
# |
||||
# Топология (3 узла, как в run_test.sh): |
||||
# burst_peer.py --client (SO_MARK=1) → 10.200.100.1 → tun_test_proxy |
||||
# → tcp_proxy_client (lwIP) → ETCP → intermediate (транзит) → ETCP → exit |
||||
# → tcp_proxy_server → connect(10.200.100.1) → iptables DNAT(!mark) → 127.0.0.1:KA_PORT |
||||
# → burst_peer.py --server |
||||
# |
||||
# Проверяется: оба направления шлют известную псевдослучайную последовательность |
||||
# случайными пачками (10Б..1МБ) со случайными паузами (100..500мс), принимающая сторона |
||||
# сверяет каждый байт. Если за stall_ms нет прогресса при неполном приёме — «застряло». |
||||
# |
||||
# Запуск: sudo ./run_burst_test.sh |
||||
|
||||
set -euo pipefail |
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" |
||||
UTUN_BIN="$SCRIPT_DIR/../../utun" |
||||
LOG_DIR="$SCRIPT_DIR/log" |
||||
BURST_PY="$SCRIPT_DIR/burst_peer.py" |
||||
|
||||
KA_PORT=19193 |
||||
BURST_IP="10.200.100.1" |
||||
|
||||
NAME="${BURST_NAME:-burst}" |
||||
CONNECTIONS="${BURST_CONNECTIONS:-1}" |
||||
TOTAL="${BURST_TOTAL:-4194304}" # байт на направление на соединение |
||||
STALL_MS="${BURST_STALL_MS:-2000}" |
||||
MIN_CHUNK="${BURST_MIN_CHUNK:-10}" |
||||
MAX_CHUNK="${BURST_MAX_CHUNK:-1048576}" |
||||
MIN_IDLE_MS="${BURST_MIN_IDLE_MS:-100}" |
||||
MAX_IDLE_MS="${BURST_MAX_IDLE_MS:-500}" |
||||
CLIENT_TIMEOUT="${BURST_CLIENT_TIMEOUT:-180}" |
||||
|
||||
# -------- setup / cleanup -------- |
||||
|
||||
setup_net() { |
||||
echo "=== Setting up networking ===" |
||||
sysctl -w net.ipv4.conf.all.rp_filter=0 |
||||
|
||||
ip rule add fwmark 1 priority 199 table 100 2>/dev/null || true |
||||
local gw |
||||
gw=$(ip route show default | awk '/via/ {print $3; exit}') |
||||
[ -n "$gw" ] && ip route replace default via "$gw" table 100 2>/dev/null || true |
||||
|
||||
iptables -t nat -C OUTPUT -d "$BURST_IP" -p tcp --dport "$KA_PORT" -m mark ! --mark 1 -j DNAT --to-destination 127.0.0.1:"$KA_PORT" 2>/dev/null \ |
||||
|| iptables -t nat -A OUTPUT -d "$BURST_IP" -p tcp --dport "$KA_PORT" -m mark ! --mark 1 -j DNAT --to-destination 127.0.0.1:"$KA_PORT" |
||||
echo " Done" |
||||
} |
||||
|
||||
setup_tun_route() { |
||||
for i in $(seq 1 60); do |
||||
if [ -e "/proc/sys/net/ipv4/conf/tun_test_proxy/rp_filter" ]; then |
||||
sysctl -w net.ipv4.conf.tun_test_proxy.rp_filter=0 |
||||
ip route replace 10.200.100.0/24 dev tun_test_proxy table 100 |
||||
ip route replace 10.200.100.0/24 dev tun_test_proxy # main table anti-martian |
||||
echo " tun route ready (${i}x0.3s)" |
||||
return 0 |
||||
fi |
||||
sleep 0.3 |
||||
done |
||||
echo " WARN: tun_test_proxy not found after 18s" |
||||
} |
||||
|
||||
cleanup_net() { |
||||
echo "=== Cleaning up networking ===" |
||||
iptables -t nat -D OUTPUT -d "$BURST_IP" -p tcp --dport "$KA_PORT" -m mark ! --mark 1 -j DNAT --to-destination 127.0.0.1:"$KA_PORT" 2>/dev/null || true |
||||
ip rule del fwmark 1 priority 199 table 100 2>/dev/null || true |
||||
ip route del 10.200.100.0/24 dev tun_test_proxy table 100 2>/dev/null || true |
||||
ip route del 10.200.100.0/24 dev tun_test_proxy 2>/dev/null || true |
||||
ip route flush table 100 2>/dev/null || true |
||||
sysctl -w net.ipv4.conf.all.rp_filter=1 |
||||
sysctl -w net.ipv4.conf.tun_test_proxy.rp_filter=1 2>/dev/null || true |
||||
echo " Done" |
||||
} |
||||
|
||||
cleanup() { |
||||
echo ""; echo "=== Cleanup ===" |
||||
kill $SERVER_PID 2>/dev/null || true |
||||
kill $EXIT_PID 2>/dev/null || true |
||||
kill $INTER_PID 2>/dev/null || true |
||||
kill $CLIENT_PID 2>/dev/null || true |
||||
wait $SERVER_PID 2>/dev/null || true |
||||
wait $EXIT_PID 2>/dev/null || true |
||||
wait $INTER_PID 2>/dev/null || true |
||||
wait $CLIENT_PID 2>/dev/null || true |
||||
sleep 0.5; cleanup_net |
||||
} |
||||
|
||||
wait_for_etcp() { |
||||
for i in $(seq 1 40); do |
||||
grep -q "Connection UP" "$LOG_DIR/exit_utun.log" 2>/dev/null \ |
||||
&& grep -q "Connection UP" "$LOG_DIR/intermediate_utun.log" 2>/dev/null \ |
||||
&& grep -q "Connection UP" "$LOG_DIR/client_utun.log" 2>/dev/null \ |
||||
&& return 0 |
||||
sleep 0.5 |
||||
done |
||||
return 1 |
||||
} |
||||
|
||||
# -------- main -------- |
||||
|
||||
if [ "$(id -u)" -ne 0 ]; then echo "ERROR: must be root"; exit 1; fi |
||||
[ -x "$UTUN_BIN" ] || { echo "ERROR: utun not found. Build first."; exit 1; } |
||||
|
||||
echo "=== tcp_proxy burst test (full-duplex, byte-exact) ===" |
||||
echo "name=$NAME connections=$CONNECTIONS total=$TOTAL/direction chunk=$MIN_CHUNK..$MAX_CHUNK idle=${MIN_IDLE_MS}..${MAX_IDLE_MS}ms stall=${STALL_MS}ms" |
||||
|
||||
mkdir -p "$LOG_DIR"; rm -f "$LOG_DIR"/burst_*.log |
||||
setup_net |
||||
|
||||
echo "Starting burst server on 127.0.0.1:$KA_PORT ..." |
||||
python3 "$BURST_PY" --server --host 127.0.0.1 --port "$KA_PORT" \ |
||||
--connections "$CONNECTIONS" --total "$TOTAL" \ |
||||
--min-chunk "$MIN_CHUNK" --max-chunk "$MAX_CHUNK" \ |
||||
--min-idle-ms "$MIN_IDLE_MS" --max-idle-ms "$MAX_IDLE_MS" \ |
||||
--stall-ms "$STALL_MS" --name "$NAME" \ |
||||
>"$LOG_DIR/burst_server.log" 2>&1 & |
||||
SERVER_PID=$!; sleep 0.3 |
||||
|
||||
echo "Starting utun exit ..." |
||||
"$UTUN_BIN" -c "$SCRIPT_DIR/exit.conf" -f -l "$LOG_DIR/exit_utun.log" >"$LOG_DIR/exit_stdout.log" 2>&1 & |
||||
EXIT_PID=$! |
||||
|
||||
echo "Starting utun intermediate ..." |
||||
"$UTUN_BIN" -c "$SCRIPT_DIR/intermediate.conf" -f -l "$LOG_DIR/intermediate_utun.log" >"$LOG_DIR/intermediate_stdout.log" 2>&1 & |
||||
INTER_PID=$! |
||||
|
||||
echo "Starting utun client ..." |
||||
"$UTUN_BIN" -c "$SCRIPT_DIR/client.conf" -f -l "$LOG_DIR/client_utun.log" >"$LOG_DIR/client_stdout.log" 2>&1 & |
||||
CLIENT_PID=$! |
||||
|
||||
trap cleanup EXIT |
||||
|
||||
setup_tun_route |
||||
|
||||
echo "Waiting for ETCP connection ..." |
||||
wait_for_etcp || { echo "ERROR: ETCP timeout"; tail -20 "$LOG_DIR/client_utun.log"; exit 1; } |
||||
echo "ETCP ready"; sleep 1 |
||||
|
||||
echo ""; echo "=== Running burst (client) ===" |
||||
set +e |
||||
timeout "$CLIENT_TIMEOUT" python3 "$BURST_PY" --host "$BURST_IP" --port "$KA_PORT" --mark 1 \ |
||||
--connections "$CONNECTIONS" --total "$TOTAL" \ |
||||
--min-chunk "$MIN_CHUNK" --max-chunk "$MAX_CHUNK" \ |
||||
--min-idle-ms "$MIN_IDLE_MS" --max-idle-ms "$MAX_IDLE_MS" \ |
||||
--stall-ms "$STALL_MS" --name "$NAME" \ |
||||
>"$LOG_DIR/burst_client.log" 2>&1 |
||||
CLIENT_RC=$? |
||||
wait "$SERVER_PID" 2>/dev/null |
||||
SERVER_RC=$? |
||||
set -e |
||||
|
||||
echo ""; echo "=== $NAME results ===" |
||||
echo " client: rc=$CLIENT_RC $(tail -1 "$LOG_DIR/burst_client.log" 2>/dev/null)" |
||||
echo " server: rc=$SERVER_RC $(tail -1 "$LOG_DIR/burst_server.log" 2>/dev/null)" |
||||
|
||||
trap - EXIT; cleanup |
||||
|
||||
if [ "$CLIENT_RC" -eq 0 ] && [ "$SERVER_RC" -eq 0 ]; then |
||||
echo "$NAME test: PASS" |
||||
exit 0 |
||||
else |
||||
echo "$NAME test: FAIL (client_rc=$CLIENT_RC server_rc=$SERVER_RC)" |
||||
echo " Logs: $LOG_DIR/burst_client.log, $LOG_DIR/burst_server.log, $LOG_DIR/client_utun.log" |
||||
exit 1 |
||||
fi |
||||
@ -0,0 +1,21 @@
|
||||
#!/bin/bash |
||||
# run_load_test.sh — нагрузочный тест tcp_proxy: N параллельных соединений, |
||||
# двусторонний byte-exact трафик без idle, замер пропускной способности. |
||||
# |
||||
# Это пресет поверх run_burst_test.sh (тот же движок, другие параметры). |
||||
# |
||||
# Параметры (env): |
||||
# LOAD_CONNECTIONS — число параллельных соединений (по умолчанию 8) |
||||
# LOAD_TOTAL — байт на направление на соединение (по умолчанию 8MB) |
||||
# |
||||
# Запуск: sudo ./run_load_test.sh |
||||
|
||||
set -euo pipefail |
||||
|
||||
BURST_NAME=load \ |
||||
BURST_CONNECTIONS="${LOAD_CONNECTIONS:-8}" \ |
||||
BURST_TOTAL="${LOAD_TOTAL:-8388608}" \ |
||||
BURST_MIN_IDLE_MS=0 \ |
||||
BURST_MAX_IDLE_MS=0 \ |
||||
BURST_STALL_MS="${LOAD_STALL_MS:-30000}" \ |
||||
bash "$(dirname "$0")/run_burst_test.sh" |
||||
Loading…
Reference in new issue