From 88340197cac6ef66f49113e2733404bcafad8140 Mon Sep 17 00:00:00 2001 From: aya Date: Fri, 26 Sep 2025 01:22:01 +0300 Subject: [PATCH 1/7] rework old scripts & merge scripts --- .../store/Store_multinodes.sh | 87 --------- .../store/nodes_15_service_1.sh | 139 +++++++++++++ .../store/store_15nodes_scenario1.sh | 111 ----------- .../store/store_15nodes_scenario2.sh | 59 ------ .../store/store_15nodes_stress_scenario.sh | 182 ------------------ .../store/store_cpu_stress.sh | 114 +++++++++++ .../store/store_different_payloads.sh | 82 -------- .../store/store_multi-topic.sh | 92 --------- .../store/store_scenario1.sh | 130 ------------- .../store/store_scenario6.sh | 124 ++++++++++++ .../store/store_stress1.sh | 96 --------- .../store/store_stress2.sh | 97 ---------- .../store/store_stress3.sh | 57 ------ .../store/store_stress_publishing.sh | 88 --------- .../store/stress1_high_concurrency.sh | 143 ++++++++++++++ 15 files changed, 520 insertions(+), 1081 deletions(-) delete mode 100644 scripts/Lite_protocol_scripts/store/Store_multinodes.sh create mode 100755 scripts/Lite_protocol_scripts/store/nodes_15_service_1.sh delete mode 100644 scripts/Lite_protocol_scripts/store/store_15nodes_scenario1.sh delete mode 100644 scripts/Lite_protocol_scripts/store/store_15nodes_scenario2.sh delete mode 100644 scripts/Lite_protocol_scripts/store/store_15nodes_stress_scenario.sh create mode 100755 scripts/Lite_protocol_scripts/store/store_cpu_stress.sh delete mode 100644 scripts/Lite_protocol_scripts/store/store_different_payloads.sh delete mode 100644 scripts/Lite_protocol_scripts/store/store_multi-topic.sh delete mode 100755 scripts/Lite_protocol_scripts/store/store_scenario1.sh create mode 100755 scripts/Lite_protocol_scripts/store/store_scenario6.sh delete mode 100644 scripts/Lite_protocol_scripts/store/store_stress1.sh delete mode 100644 scripts/Lite_protocol_scripts/store/store_stress2.sh delete mode 100644 scripts/Lite_protocol_scripts/store/store_stress3.sh delete mode 100644 scripts/Lite_protocol_scripts/store/store_stress_publishing.sh create mode 100755 scripts/Lite_protocol_scripts/store/stress1_high_concurrency.sh diff --git a/scripts/Lite_protocol_scripts/store/Store_multinodes.sh b/scripts/Lite_protocol_scripts/store/Store_multinodes.sh deleted file mode 100644 index ce65c241..00000000 --- a/scripts/Lite_protocol_scripts/store/Store_multinodes.sh +++ /dev/null @@ -1,87 +0,0 @@ -#!/bin/bash -set -e -# Stress Store with multiple Store nodes while running phased LPT publishers/receivers. - -STORE_NODES="/ip4/10.2.0.101/tcp/60001/p2p/16Uiu2HAkyte8uj451tGkbww4Mjcg6DRnmAHxNeWyF4zp23RbpG3n,/ip4/10.2.0.102/tcp/60001/p2p/16Uiu2HA7abcDEF451tGkbzz4Mjcg6DRnmAHxNeWyF4zp23RbpXYZ2,/ip4/10.2.0.103/tcp/60001/p2p/16Uiu2HA9LMNoPQ451tGkbww4Mjcg6DRnmAHxNeWyF4zp23Rbppppp" -RELAY_NODE_REST_ADDRESS="http://127.0.0.1:8645" -STORE_NODE_REST_ADDRESS="http://127.0.0.1:8644" -PUBSUB_TOPIC="/waku/2/default-waku/proto" -CONTENT_TOPIC="/sonda/2/polls/proto" -PHASE_SLEEP=300 - -echo "Running test..." -current_time=$(date +"%Y-%m-%d %H:%M:%S") -echo "Bringing up simulator at $current_time" - -cd ./waku-simulator -export SERVICENODE_CPU_CORES=0 -export POSTGRES_CPU_CORES=1-3 -export GF_SECURITY_ADMIN_USER=admin -export GF_SECURITY_ADMIN_PASSWORD=admin -docker compose up -d -while [ "$(docker inspect --format "{{.State.Status}}" $(docker compose ps -q servicenode))" != "running" ]; do - sleep 1 -done -cd .. - -cd ./sonda -docker build -t local-perf-sonda -f ./Dockerfile.sonda . -cat > ./perf-test.env </dev/null || true)" + [[ "$state" == "running" ]] && break + fi + sleep 1 +done + +cd ../lpt + +# -------------------- LPT common knobs (same exports) ------------------------- +export LPT_IMAGE=harbor.status.im/wakuorg/liteprotocoltester:latest +export START_PUBLISHING_AFTER=15 +export NUM_MESSAGES=0 +export MESSAGE_INTERVAL_MILLIS=100 + +export MIN_MESSAGE_SIZE=120Kb +export MAX_MESSAGE_SIZE=145Kb + +export LIGHTPUSH_SERVICE_PEER=/ip4/10.2.0.101/tcp/60001/p2p/16Uiu2HAkyte8uj451tGkbww4Mjcg6DRnmAHxNeWyF4zp23RbpG3n +export FILTER_SERVICE_PEER=/ip4/10.2.0.101/tcp/60001/p2p/16Uiu2HAkyte8uj451tGkbww4Mjcg6DRnmAHxNeWyF4zp23RbpG3n + +export PUBSUB=/waku/2/rs/66/0 +export CONTENT_TOPIC=/tester/2/light-pubsub-test/wakusim +export CLUSTER_ID=66 + +# wait time before starting traffic +sleep 60 + +# Start LPT for Phase 1 will happen after Sonda is up + +# -------------------- Sonda (Store monitor) ----------------------------------- +cd ../sonda + +docker build -t local-perf-sonda -f ./Dockerfile.sonda . + +# perf-test.env (same style as scenario_1.sh) +cat < perf-test.env +RELAY_NODE_REST_ADDRESS=http://127.0.0.1:8645 +STORE_NODE_REST_ADDRESS=http://127.0.0.1:8644 +QUERY_DELAY=0.5 +STORE_NODES=/ip4/10.2.0.101/tcp/60001/p2p/16Uiu2HAkyte8uj451tGkbww4Mjcg6DRnmAHxNeWyF4zp23RbpG3n +CLUSTER_ID=66 +SHARD=0 +EOF + +sleep 5 + +docker rm -f sonda >/dev/null 2>&1 || true +docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda + +cd ../lpt + +# -------------------- Phase 1: 6 pub / 6 recv -------------------------------- +export NUM_PUBLISHER_NODES=6 +export NUM_RECEIVER_NODES=6 +docker compose down -v >/dev/null 2>&1 || true +docker compose up -d + +current_time=$(date +"%Y-%m-%d %H:%M:%S") +echo "[store_scenario1] LPT is running with 6 publishers and 6 receivers + sonda from now: $current_time" + +sleep 120 + +# -------------------- Phase 2: 3 pub / 12 recv ------------------------------- +docker compose down -v +export NUM_PUBLISHER_NODES=3 +export NUM_RECEIVER_NODES=12 +docker compose up -d + +current_time=$(date +"%Y-%m-%d %H:%M:%S") +echo "[store_scenario1] LPT is running with 3 publishers and 12 receivers from now: $current_time" + +sleep 120 + +# -------------------- Phase 3: 12 pub / 3 recv ------------------------------- +docker compose down -v +export NUM_PUBLISHER_NODES=12 +export NUM_RECEIVER_NODES=3 +docker compose up -d + +current_time=$(date +"%Y-%m-%d %H:%M:%S") +echo "[store_scenario1] LPT is running with 12 publishers and 3 receivers from now: $current_time" + +sleep 120 + +# -------------------- Phase 4: receivers down; keep publisher + sonda -------- +docker compose down -v +export NUM_PUBLISHER_NODES=12 +export NUM_RECEIVER_NODES=0 +docker compose up -d + +current_time=$(date +"%Y-%m-%d %H:%M:%S") +echo "[store_scenario1] LPT receivers are down; sonda and lightpush publisher running from now: $current_time" + +sleep 120 + +# -------------------- Phase 5: LPT down; only sonda -------------------------- +docker compose down -v + +current_time=$(date +"%Y-%m-%d %H:%M:%S") +echo "[store_scenario1] LPT down; only sonda is working from now: $current_time" + +sleep 120 + +cd .. + +current_time=$(date +"%Y-%m-%d %H:%M:%S") +echo "[store_scenario1] Test finished at $current_time" + +# finish +# exec ./stop_test.sh diff --git a/scripts/Lite_protocol_scripts/store/store_15nodes_scenario1.sh b/scripts/Lite_protocol_scripts/store/store_15nodes_scenario1.sh deleted file mode 100644 index b3360cb8..00000000 --- a/scripts/Lite_protocol_scripts/store/store_15nodes_scenario1.sh +++ /dev/null @@ -1,111 +0,0 @@ -#!/bin/bash - -set -e - -# This implements a waku-simulator with 15 nodes network, 1 service, 1 edge node added -# service node is limited to 1 cpu core with only 512 MB -# Runs different test phases with different load from sonda (STORE) side -# 1. 2 sonda instances, query every 500ms -# 2. 5 sonda instances, query every 200ms -# 3. 10 sonda instances, query every 100ms - -echo "Running test..." - -current_time=$(date +"%Y-%m-%d %H:%M:%S") -echo "Bringing up simulator at $current_time" - -cd ./waku-simulator - -# simulator exports -export NUM_NWAKU_NODES=15 -export TRAFFIC_DELAY_SECONDS=15 -export MSG_SIZE_KBYTES=10 -export SERVICENODE_CPU_CORES="0-3" -export POSTGRES_CPU_CORES="0-3" - -docker compose up -d -cd .. - -echo "Waiting 30s for service node to be ready..." -sleep 30 - -cd ./sonda - -# build sonda image -docker build -t local-perf-sonda -f Dockerfile.sonda . - -# 2 sondas 500ms - -cat < perf-test.env -RELAY_NODE_REST_ADDRESS=http://127.0.0.1:8645 -STORE_NODE_REST_ADDRESS=http://127.0.0.1:8644 -QUERY_DELAY=0.5 -STORE_NODES=/ip4/127.0.0.1/tcp/60001/p2p/ -CLUSTER_ID=66 -SHARD=0 -HEALTH_THRESHOLD=0.95 -EOF - -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda - -echo "Phase 1 running 300s..." -sleep 300 -docker kill $(docker ps -q -f "label=sonda") >/dev/null 2>&1 || true - -#5 sondas 200ms - -cat < perf-test.env -RELAY_NODE_REST_ADDRESS=http://127.0.0.1:8645 -STORE_NODE_REST_ADDRESS=http://127.0.0.1:8644 -QUERY_DELAY=0.2 -STORE_NODES=/ip4/127.0.0.1/tcp/60001/p2p/ -CLUSTER_ID=66 -SHARD=0 -HEALTH_THRESHOLD=0.95 -EOF - -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda - -echo "Phase 2 running 300s..." -sleep 300 -docker kill $(docker ps -q -f "label=sonda") >/dev/null 2>&1 || true - -#10 sondas 100ms -cat < perf-test.env -RELAY_NODE_REST_ADDRESS=http://127.0.0.1:8645 -STORE_NODE_REST_ADDRESS=http://127.0.0.1:8644 -QUERY_DELAY=0.1 -STORE_NODES=/ip4/127.0.0.1/tcp/60001/p2p/ -CLUSTER_ID=66 -SHARD=0 -HEALTH_THRESHOLD=0.95 -EOF - -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda - -echo "Phase 3 running 300s..." -sleep 300 -docker kill $(docker ps -q -f "label=sonda") >/dev/null 2>&1 || true - -cd .. - -cd ./waku-simulator -docker compose down -cd .. - -current_time=$(date +"%Y-%m-%d %H:%M:%S") -echo "Test finished at $current_time" diff --git a/scripts/Lite_protocol_scripts/store/store_15nodes_scenario2.sh b/scripts/Lite_protocol_scripts/store/store_15nodes_scenario2.sh deleted file mode 100644 index ae7a5989..00000000 --- a/scripts/Lite_protocol_scripts/store/store_15nodes_scenario2.sh +++ /dev/null @@ -1,59 +0,0 @@ -#!/bin/bash - -set -e - -echo "Running test..." - -# This implements a short version of 1st store scenario. -# waku-simulator with 15 nodes network, 1 service, 1 edge node added -# service node is limited to 1 cpu core with only 512 MB -# Runs 1 phase with sonda load: -# 1. 10 sonda instances, each queries in every 100ms - -cd ./waku-simulator - -export NUM_NWAKU_NODES=15 -export TRAFFIC_DELAY_SECONDS=10 -export MSG_SIZE_KBYTES=8 - -docker compose up -d -cd .. - -echo "Waiting 20s for service node..." -sleep 20 - -cd ./sonda - -docker build -t local-perf-sonda -f Dockerfile.sonda . - -cat < perf-test.env -RELAY_NODE_REST_ADDRESS=http://127.0.0.1:8645 -STORE_NODE_REST_ADDRESS=http://127.0.0.1:8644 -QUERY_DELAY=0.1 -STORE_NODES=/ip4/127.0.0.1/tcp/60001/p2p/ -CLUSTER_ID=66 -SHARD=0 -HEALTH_THRESHOLD=0.95 -EOF - -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda - -echo "Phase running 300s..." -sleep 300 - -docker kill $(docker ps -q -f "label=sonda") >/dev/null 2>&1 || true - -cd .. - -cd ./waku-simulator -docker compose down -cd .. diff --git a/scripts/Lite_protocol_scripts/store/store_15nodes_stress_scenario.sh b/scripts/Lite_protocol_scripts/store/store_15nodes_stress_scenario.sh deleted file mode 100644 index f2a68521..00000000 --- a/scripts/Lite_protocol_scripts/store/store_15nodes_stress_scenario.sh +++ /dev/null @@ -1,182 +0,0 @@ -#!/bin/bash - - -# waku-simulator with 15 nodes network, 1 service, 1 edge node added -# service node is limited to 1 cpu core with only 512 MB -# This scenario intended to stress test service node STORE queries with high request frequency -# Increasing number of sonda instances and decreasing query delay ms -# 16 sondas 100ms -# 16 sondas 50ms -# 24 sondas 50ms -# 24 sondas 20ms - -set -e - -echo "Running test..." - -cd ./waku-simulator - -export NUM_NWAKU_NODES=15 -export TRAFFIC_DELAY_SECONDS=10 -export MSG_SIZE_KBYTES=12 - -docker compose up -d -cd .. - -echo "Waiting 30s for service node..." -sleep 30 - -cd ./sonda - -docker build -t local-perf-sonda -f Dockerfile.sonda . - -#16 sondas 100ms -cat < perf-test.env -RELAY_NODE_REST_ADDRESS=http://127.0.0.1:8645 -STORE_NODE_REST_ADDRESS=http://127.0.0.1:8644 -QUERY_DELAY=0.1 -STORE_NODES=/ip4/127.0.0.1/tcp/60001/p2p/ -CLUSTER_ID=66 -SHARD=0 -HEALTH_THRESHOLD=0.95 -EOF - -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda - -echo "Phase 1 300s..." -sleep 300 -docker kill $(docker ps -q -f "label=sonda") >/dev/null 2>&1 || true - -# 16 sondas 50ms -cat < perf-test.env -RELAY_NODE_REST_ADDRESS=http://127.0.0.1:8645 -STORE_NODE_REST_ADDRESS=http://127.0.0.1:8644 -QUERY_DELAY=0.05 -STORE_NODES=/ip4/127.0.0.1/tcp/60001/p2p/ -CLUSTER_ID=66 -SHARD=0 -HEALTH_THRESHOLD=0.95 -EOF - -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda - -echo "Phase 2 300s..." -sleep 300 -docker kill $(docker ps -q -f "label=sonda") >/dev/null 2>&1 || true - -# 24 sondas 50ms -cat < perf-test.env -RELAY_NODE_REST_ADDRESS=http://127.0.0.1:8645 -STORE_NODE_REST_ADDRESS=http://127.0.0.1:8644 -QUERY_DELAY=0.05 -STORE_NODES=/ip4/127.0.0.1/tcp/60001/p2p/ -CLUSTER_ID=66 -SHARD=0 -HEALTH_THRESHOLD=0.95 -EOF - -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda - -echo "Phase 3 300s..." -sleep 300 -docker kill $(docker ps -q -f "label=sonda") >/dev/null 2>&1 || true - -#24 sondas 20ms -cat < perf-test.env -RELAY_NODE_REST_ADDRESS=http://127.0.0.1:8645 -STORE_NODE_REST_ADDRESS=http://127.0.0.1:8644 -QUERY_DELAY=0.02 -STORE_NODES=/ip4/127.0.0.1/tcp/60001/p2p/ -CLUSTER_ID=66 -SHARD=0 -HEALTH_THRESHOLD=0.95 -EOF - -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -# repeat 23 more times -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda -docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda - -echo "Phase 4 300s..." -sleep 300 -docker kill $(docker ps -q -f "label=sonda") >/dev/null 2>&1 || true - -cd .. - -cd ./waku-simulator -docker compose down -cd .. diff --git a/scripts/Lite_protocol_scripts/store/store_cpu_stress.sh b/scripts/Lite_protocol_scripts/store/store_cpu_stress.sh new file mode 100755 index 00000000..05536c0f --- /dev/null +++ b/scripts/Lite_protocol_scripts/store/store_cpu_stress.sh @@ -0,0 +1,114 @@ +#!/bin/bash +set -e + +# ========================= +# IDs (same style) +# ========================= +export SCENARIO_ID="cpu_store_stress" +export TEST_NAME="${SCENARIO_ID}_$(date +%Y%m%d_%H%M%S)" +echo "[${TEST_NAME}] start" + +# ========================= +# Images (match your scripts) +# ========================= +export LPT_IMAGE="harbor.status.im/wakuorg/liteprotocoltester:latest" + +# ========================= +# Compose-required vars +# ========================= +export GF_SECURITY_ADMIN_USER="admin" +export GF_SECURITY_ADMIN_PASSWORD="admin" +export NODEKEY="${NODEKEY:-}" +export STORAGE_SIZE="${STORAGE_SIZE:-}" + +# ========================= +# Resource knobs +# ========================= +export SERVICENODE_CPU_CORES="0-1" +export POSTGRES_CPU_CORES="2-3" +export SERVICE_MEM_LIMIT="2g" +export POSTGRES_MEM_LIMIT="2g" +export POSTGRES_SHM_SIZE="1g" + +# ========================= +# Topic / shard +# ========================= +export CLUSTER_ID=66 +export SHARD=0 +export PUBSUB_TOPIC="/waku/2/rs/${CLUSTER_ID}/${SHARD}" +export CONTENT_TOPIC="/sonda/2/polls/proto" + +# ========================= +# REST endpoints +# ========================= +export RELAY_NODE_REST_ADDRESS="http://127.0.0.1:8645" +export STORE_NODE_REST_ADDRESS="http://127.0.0.1:8644" + +# ========================= +# Phase 0 — bring up simulator +# ========================= +echo "[${TEST_NAME}] Phase 0: bring up simulator" +cd ./waku-simulator +docker compose up -d +cd .. +echo "[${TEST_NAME}] wait 30s" +sleep 30 + +# ========================= +# Phase 1 — CPU-heavy writers (small msgs, high rate) +# ========================= +export NUM_PUBLISHER_NODES=24 +export NUM_RECEIVER_NODES=8 +export MESSAGE_INTERVAL_MILLIS=10 +export MIN_MESSAGE_SIZE=256 +export MAX_MESSAGE_SIZE=1024 +export START_PUBLISHING_AFTER=10 +export NUM_MESSAGES=0 + +echo "[${TEST_NAME}] Phase 1: start writers" +docker run -d --rm --name lpt_cpu \ + -e PUB_NODES=${NUM_PUBLISHER_NODES} \ + -e RCV_NODES=${NUM_RECEIVER_NODES} \ + -e MSG_INTERVAL_MS=${MESSAGE_INTERVAL_MILLIS} \ + -e MIN_MSG=${MIN_MESSAGE_SIZE} \ + -e MAX_MSG=${MAX_MESSAGE_SIZE} \ + -e PUBSUB_TOPIC=${PUBSUB_TOPIC} \ + --network host ${LPT_IMAGE} + +# ========================= +# Phase 2 — Sonda (exact style from your scripts) +# build image + write env + run with --env-file +# ========================= +echo "[${TEST_NAME}] Phase 2: build and start Sonda" +cd ../sonda +docker build -t local-perf-sonda -f ./Dockerfile.sonda . + +cat < perf-test.env +RELAY_NODE_REST_ADDRESS=http://127.0.0.1:8645 +STORE_NODE_REST_ADDRESS=http://127.0.0.1:8644 +QUERY_DELAY=0.5 +STORE_NODES=/ip4/10.2.0.101/tcp/60001/p2p/16Uiu2HAkyte8uj451tGkbww4Mjcg6DRnmAHxNeWyF4zp23RbpG3n +CLUSTER_ID=66 +SHARD=0 +EOF + +docker run --env-file perf-test.env -l sonda -d --network host local-perf-sonda +cd ../waku-lite/waku-protocol-perf-test + +echo "[${TEST_NAME}] hold 12m" +sleep 720 + +# ========================= +# Phase 3 — recovery +# ========================= +echo "[${TEST_NAME}] Phase 3: stop writers; observe 120s" +docker kill lpt_cpu || true +sleep 120 + +# ========================= +# Cleanup +# ========================= +echo "[${TEST_NAME}] cleanup" +docker kill $(docker ps -q --filter "label=sonda") || true +cd ./waku-simulator +docker compose down -v diff --git a/scripts/Lite_protocol_scripts/store/store_different_payloads.sh b/scripts/Lite_protocol_scripts/store/store_different_payloads.sh deleted file mode 100644 index 6b2e5985..00000000 --- a/scripts/Lite_protocol_scripts/store/store_different_payloads.sh +++ /dev/null @@ -1,82 +0,0 @@ -#!/bin/bash -# Scenario 6: Alternate tiny and huge payloads at fast rates to stress allocators and CPU. -set -e - -STORE_NODES="/ip4/10.2.0.101/tcp/60001/p2p/16Uiu2HAkyte8uj451tGkbww4Mjcg6DRnmAHxNeWyF4zp23RbpG3n" -RELAY_NODE_REST_ADDRESS="http://127.0.0.1:8645" -STORE_NODE_REST_ADDRESS="http://127.0.0.1:8644" -PUBSUB_TOPIC="/waku/2/default-waku/proto" -CONTENT_TOPIC="/sonda/2/polls/proto" -PHASE_SLEEP=240 - -echo "Running test..." -current_time=$(date +"%Y-%m-%d %H:%M:%S") -echo "Bringing up simulator at $current_time" - -cd ./waku-simulator -export SERVICENODE_CPU_CORES=0 -export POSTGRES_CPU_CORES=1-3 -export GF_SECURITY_ADMIN_USER=admin -export GF_SECURITY_ADMIN_PASSWORD=admin -docker compose up -d -while [ "$(docker inspect --format "{{.State.Status}}" $(docker compose ps -q servicenode))" != "running" ]; do - sleep 1 -done -cd .. - -cd ./sonda -docker build -t local-perf-sonda -f ./Dockerfile.sonda . -cat > ./perf-test.env < ./perf-topic-a.env < ./perf-topic-b.env < 1x5 -> 5x1 -> receivers down -> lpt down -# ------------------------------------------------------------------------------ - -# >>> EDIT THESE IF YOUR SETUP DIFFERS <<< -STORE_NODES="/ip4/10.2.0.101/tcp/60001/p2p/16Uiu2HAkyte8uj451tGkbww4Mjcg6DRnmAHxNeWyF4zp23RbpG3n" -RELAY_NODE_REST_ADDRESS="http://127.0.0.1:8645" -STORE_NODE_REST_ADDRESS="http://127.0.0.1:8644" -PUBSUB_TOPIC="/waku/2/default-waku/proto" -CONTENT_TOPIC="/sonda/2/polls/proto" -# <<< EDIT ABOVE >>> - -echo "Running test..." - -current_time=$(date +"%Y-%m-%d %H:%M:%S") -echo "Bringing up simulator at $current_time" - -cd ./waku-simulator - -# same style: exports before compose -export SERVICENODE_CPU_CORES=0 # 1 core for service node -export POSTGRES_CPU_CORES=1-3 # keep DB off core 0 -export GF_SECURITY_ADMIN_USER=admin -export GF_SECURITY_ADMIN_PASSWORD=admin -export NWAKU_IMAGE=wakuorg/nwaku:latest -export NUM_NWAKU_NODES=15 -export RLN_ENABLED=false - -docker compose up -d - -# Wait for servicenode to be running (scenario_1.sh style) -while [ "$(docker inspect --format "{{.State.Status}}" $(docker compose ps -q servicenode))" != "running" ]; do - sleep 1 -done - -cd .. - -# ------------------------------------------------------------------------------ -# Start Sonda (like scenario_1.sh: build & run with env-file, host network) -# ------------------------------------------------------------------------------ -cd ./sonda - -docker build -t local-perf-sonda -f ./Dockerfile.sonda . - -cat > ./perf-test.env < per-message CPU (encode/verify/route) dominates. +export NUM_PUBLISHER_NODES=24 +export NUM_RECEIVER_NODES=8 # keep some receivers to drive end-to-end +export MESSAGE_INTERVAL_MILLIS=8 # ~125 msg/s per publisher (adjust if too hot) +export MIN_MESSAGE_SIZE=256 # bytes +export MAX_MESSAGE_SIZE=1024 # bytes +export START_PUBLISHING_AFTER=10 +export NUM_MESSAGES=0 # unlimited + +echo "[${TEST_NAME}] phase1 writers: ${NUM_PUBLISHER_NODES} pubs @ ${MESSAGE_INTERVAL_MILLIS}ms, 256-1024B" +docker run -d --rm --name lpt_cpu \ + -e PUB_NODES=${NUM_PUBLISHER_NODES} \ + -e RCV_NODES=${NUM_RECEIVER_NODES} \ + -e MSG_INTERVAL_MS=${MESSAGE_INTERVAL_MILLIS} \ + -e MIN_MSG=${MIN_MESSAGE_SIZE} \ + -e MAX_MSG=${MAX_MESSAGE_SIZE} \ + -e PUBSUB_TOPIC=${PUBSUB_TOPIC} \ + --network host ${LPT_IMAGE} + +# ---------- Phase 2: Store read hammer (concurrent readers) ---------- +# Mix includeData true/false and page sizes to exercise CPU (serialization/JSON) & DB. +echo "[${TEST_NAME}] phase2 store readers" +docker run -d --rm --name sonda_idx \ + --network host ${SONDA_IMAGE} \ + --relay-node-rest-address "${RELAY_NODE_REST_ADDRESS}" \ + --store-node-rest-address "${STORE_NODE_REST_ADDRESS}" \ + --pubsub-topic "${PUBSUB_TOPIC}" \ + --store-nodes "${STORE_NODES}" \ + --delay-seconds 0.07 --health-threshold ${HEALTH_THRESHOLD} \ + --metrics-port 8004 --include-data=false --page-size 150 + +docker run -d --rm --name sonda_smallpages \ + --network host ${SONDA_IMAGE} \ + --relay-node-rest-address "${RELAY_NODE_REST_ADDRESS}" \ + --store-node-rest-address "${STORE_NODE_REST_ADDRESS}" \ + --pubsub-topic "${PUBSUB_TOPIC}" \ + --store-nodes "${STORE_NODES}" \ + --delay-seconds 0.05 --health-threshold ${HEALTH_THRESHOLD} \ + --metrics-port 8005 --include-data=true --page-size 5 + +docker run -d --rm --name sonda_bigpages \ + --network host ${SONDA_IMAGE} \ + --relay-node-rest-address "${RELAY_NODE_REST_ADDRESS}" \ + --store-node-rest-address "${STORE_NODE_REST_ADDRESS}" \ + --pubsub-topic "${PUBSUB_TOPIC}" \ + --store-nodes "${STORE_NODES}" \ + --delay-seconds 0.05 --health-threshold ${HEALTH_THRESHOLD} \ + --metrics-port 8006 --include-data=true --page-size 50 + +# Extra readers to push CPU on the service process: +docker run -d --rm --name sonda_mix1 \ + --network host ${SONDA_IMAGE} \ + --relay-node-rest-address "${RELAY_NODE_REST_ADDRESS}" \ + --store-node-rest-address "${STORE_NODE_REST_ADDRESS}" \ + --pubsub-topic "${PUBSUB_TOPIC}" \ + --store-nodes "${STORE_NODES}" \ + --delay-seconds 0.03 --health-threshold ${HEALTH_THRESHOLD} \ + --metrics-port 8007 --include-data=true --page-size 25 + +docker run -d --rm --name sonda_mix2 \ + --network host ${SONDA_IMAGE} \ + --relay-node-rest-address "${RELAY_NODE_REST_ADDRESS}" \ + --store-node-rest-address "${STORE_NODE_REST_ADDRESS}" \ + --pubsub-topic "${PUBSUB_TOPIC}" \ + --store-nodes "${STORE_NODES}" \ + --delay-seconds 0.03 --health-threshold ${HEALTH_THRESHOLD} \ + --metrics-port 8008 --include-data=false --page-size 200 + +echo "[${TEST_NAME}] hold 12m" +sleep 720 + +# ---------- Phase 3: plateau & recovery ---------- +echo "[${TEST_NAME}] phase3 stop writers; keep readers 2m (recovery CPU/GC)" +docker kill lpt_cpu || true +sleep 120 + +# ---------- Cleanup ---------- +echo "[${TEST_NAME}] cleanup" +docker kill sonda_idx sonda_smallpages sonda_bigpages sonda_mix1 sonda_mix2 || true +cd ./waku-simulator +docker compose down -v diff --git a/scripts/Lite_protocol_scripts/store/store_stress1.sh b/scripts/Lite_protocol_scripts/store/store_stress1.sh deleted file mode 100644 index ddee3d1e..00000000 --- a/scripts/Lite_protocol_scripts/store/store_stress1.sh +++ /dev/null @@ -1,96 +0,0 @@ -#!/bin/bash -set -e - -STORE_NODES="/ip4/10.2.0.101/tcp/60001/p2p/16Uiu2HAkyte8uj451tGkbww4Mjcg6DRnmAHxNeWyF4zp23RbpG3n,/ip4/10.2.0.102/tcp/60001/p2p/16Uiu2HA7abcDEF451tGkbzz4Mjcg6DRnmAHxNeWyF4zp23RbpXYZ2" -RELAY_NODE_REST_ADDRESS="http://127.0.0.1:8645" -STORE_NODE_REST_ADDRESS="http://127.0.0.1:8644" -PUBSUB_TOPIC="/waku/2/default-waku/proto" -CONTENT_TOPIC="/sonda/2/polls/proto" -PHASE_SLEEP=300 - -echo "Running test..." -current_time=$(date +"%Y-%m-%d %H:%M:%S") -echo "Bringing up simulator at $current_time" - -cd ./waku-simulator -export SERVICENODE_CPU_CORES=0 -export POSTGRES_CPU_CORES=1-3 -export GF_SECURITY_ADMIN_USER=admin -export GF_SECURITY_ADMIN_PASSWORD=admin -docker compose up -d -while [ "$(docker inspect --format "{{.State.Status}}" $(docker compose ps -q servicenode))" != "running" ]; do - sleep 1 -done -cd .. - -cd ./sonda -docker build -t local-perf-sonda -f ./Dockerfile.sonda . -cat > ./perf-test.env < ./perf-test.env < ./perf-test.env < ./perf-test.env </dev/null || true)" + [[ "$state" == "running" ]] && break + fi + sleep 1 +done + +cd ../lpt + +# -------------------- LPT config --------------------------- +export LPT_IMAGE=harbor.status.im/wakuorg/liteprotocoltester:latest +export START_PUBLISHING_AFTER=15 +export NUM_MESSAGES=0 +export MESSAGE_INTERVAL_MILLIS=100 + +# Service peers +export LIGHTPUSH_SERVICE_PEER=/ip4/10.2.0.101/tcp/60001/p2p/16Uiu2HAkyte8uj451tGkbww4Mjcg6DRnmAHxNeWyF4zp23RbpG3n +export FILTER_SERVICE_PEER=/ip4/10.2.0.101/tcp/60001/p2p/16Uiu2HAkyte8uj451tGkbww4Mjcg6DRnmAHxNeWyF4zp23RbpG3n + +# Topics +export PUBSUB=/waku/2/rs/66/0 +export CONTENT_TOPIC=/tester/2/light-pubsub-test/wakusim +export CLUSTER_ID=66 + +# sleep before traffic +sleep 60 + +cd ../sonda +docker build -t local-perf-sonda -f ./Dockerfile.sonda . + +cat < perf-test.env +RELAY_NODE_REST_ADDRESS=http://127.0.0.1:8645 +STORE_NODE_REST_ADDRESS=http://127.0.0.1:8644 +QUERY_DELAY=0.5 +STORE_NODES=/ip4/10.2.0.101/tcp/60001/p2p/16Uiu2HAkyte8uj451tGkbww4Mjcg6DRnmAHxNeWyF4zp23RbpG3n +CLUSTER_ID=66 +SHARD=0 +EOF + +sleep 5 + +# Clean up +docker rm -f sonda1 sonda2 sonda3 sonda4 sonda5 >/dev/null 2>&1 || true + +# Start a baseline Sonda (0.5s) +docker run -d --name sonda1 --network host -l sonda \ + --env-file ./perf-test.env local-perf-sonda + +cd ../lpt + +# ============================================================================ # +# PHASE 1: Moderate traffic +# LPT 6/6, 50–90 KB; Sonda x1 time 0.5s +# ============================================================================ # +export NUM_PUBLISHER_NODES=6 +export NUM_RECEIVER_NODES=6 +export MIN_MESSAGE_SIZE=50Kb +export MAX_MESSAGE_SIZE=90Kb + +docker compose down -v >/dev/null 2>&1 || true +docker compose up -d + +current_time=$(date +"%Y-%m-%d %H:%M:%S") +echo "[scenario_6_store_stress] Phase 1: LPT 6/6, 50–90KB; Sonda x1 @0.5s — $current_time" +sleep 120 + +# ============================================================================ # +# PHASE 2: Same traffic but triple query rate +# Keep LPT, add Sonda x2 more time 0.1s +# ============================================================================ # +docker run -d --name sonda2 --network host -l sonda \ + --env-file ../sonda/perf-test.env -e QUERY_DELAY=0.1 local-perf-sonda +docker run -d --name sonda3 --network host -l sonda \ + --env-file ../sonda/perf-test.env -e QUERY_DELAY=0.1 local-perf-sonda + +current_time=$(date +"%Y-%m-%d %H:%M:%S") +echo "[scenario_6_store_stress] Phase 2: add Sonda x2 @0.1s (total 3) — $current_time" +sleep 120 + +# ============================================================================ # +# PHASE 3: Heavier traffic + peak query rate +# LPT 10/10, 130–149 KB; add Sonda x2 more time 0.05s +# ============================================================================ # +docker compose down -v +export NUM_PUBLISHER_NODES=10 +export NUM_RECEIVER_NODES=10 +export MIN_MESSAGE_SIZE=130Kb +export MAX_MESSAGE_SIZE=149Kb +docker compose up -d + +docker run -d --name sonda4 --network host -l sonda \ + --env-file ../sonda/perf-test.env -e QUERY_DELAY=0.05 local-perf-sonda +docker run -d --name sonda5 --network host -l sonda \ + --env-file ../sonda/perf-test.env -e QUERY_DELAY=0.05 local-perf-sonda + +current_time=$(date +"%Y-%m-%d %H:%M:%S") +echo "[scenario_6_store_stress] Phase 3: LPT 10/10, 130–149KB; Sonda total x5 (0.5s + 0.1s + 0.05s) — $current_time" +sleep 120 + +# ============================================================================ # +# PHASE 4: Store-only +# Stop LPT; keep all Sonda x5 times +# ============================================================================ # +docker compose down -v + +current_time=$(date +"%Y-%m-%d %H:%M:%S") +echo "[scenario_6_store_stress] Phase 4: LPT down; Store-only flood via Sonda x5 — $current_time" +sleep 120 + +# -------------------- Cleanup ------------------------------------------------- +docker rm -f sonda1 sonda2 sonda3 sonda4 sonda5 >/dev/null 2>&1 || true + +cd .. + +current_time=$(date +"%Y-%m-%d %H:%M:%S") +echo "[scenario_6_store_stress] Test finished at $current_time" + +# finish +# exec ./stop_test.sh From ce8a178829e6645e6db11a07d48c1ffcc1f9a5e5 Mon Sep 17 00:00:00 2001 From: aya Date: Fri, 26 Sep 2025 10:52:12 +0300 Subject: [PATCH 2/7] Apply changes to existing scripts --- .../store/store_cpu_stress.sh | 69 +++++++++---------- ...store_scenario6.sh => store_scenario_6.sh} | 14 ++-- 2 files changed, 38 insertions(+), 45 deletions(-) rename scripts/Lite_protocol_scripts/store/{store_scenario6.sh => store_scenario_6.sh} (90%) diff --git a/scripts/Lite_protocol_scripts/store/store_cpu_stress.sh b/scripts/Lite_protocol_scripts/store/store_cpu_stress.sh index 05536c0f..ecd0546e 100755 --- a/scripts/Lite_protocol_scripts/store/store_cpu_stress.sh +++ b/scripts/Lite_protocol_scripts/store/store_cpu_stress.sh @@ -1,52 +1,45 @@ #!/bin/bash set -e -# ========================= -# IDs (same style) -# ========================= export SCENARIO_ID="cpu_store_stress" export TEST_NAME="${SCENARIO_ID}_$(date +%Y%m%d_%H%M%S)" echo "[${TEST_NAME}] start" -# ========================= -# Images (match your scripts) -# ========================= -export LPT_IMAGE="harbor.status.im/wakuorg/liteprotocoltester:latest" - -# ========================= -# Compose-required vars -# ========================= export GF_SECURITY_ADMIN_USER="admin" export GF_SECURITY_ADMIN_PASSWORD="admin" export NODEKEY="${NODEKEY:-}" export STORAGE_SIZE="${STORAGE_SIZE:-}" -# ========================= -# Resource knobs -# ========================= -export SERVICENODE_CPU_CORES="0-1" -export POSTGRES_CPU_CORES="2-3" -export SERVICE_MEM_LIMIT="2g" -export POSTGRES_MEM_LIMIT="2g" -export POSTGRES_SHM_SIZE="1g" +export NWAKU_IMAGE="wakuorg/nwaku:latest" +export NUM_NWAKU_NODES=15 +export RLN_ENABLED=false -# ========================= -# Topic / shard -# ========================= +# Service node +export SERVICENODE_METRICS_PORT=8008 +export SERVICENODE_HTTP_PORT=8644 +export SERVICENODE_REST_PORT=8645 + +export POSTGRES_EXPORTER_PORT=9187 +export PROMETHEUS_PORT=9090 +export GRAFANA_PORT=3001 + +export SERVICENODE_CPU_CORES="0-1" +export SERVICENODE_MEM_LIMIT=512m +export POSTGRES_CPU_CORES="0-3" +export POSTGRES_MEM_LIMIT=2g +export POSTGRES_SHM=1g + +export LPT_IMAGE="harbor.status.im/wakuorg/liteprotocoltester:latest" + +# Topic / shard export CLUSTER_ID=66 export SHARD=0 export PUBSUB_TOPIC="/waku/2/rs/${CLUSTER_ID}/${SHARD}" -export CONTENT_TOPIC="/sonda/2/polls/proto" -# ========================= -# REST endpoints -# ========================= -export RELAY_NODE_REST_ADDRESS="http://127.0.0.1:8645" -export STORE_NODE_REST_ADDRESS="http://127.0.0.1:8644" +export RELAY_NODE_REST_ADDRESS="http://127.0.0.1:${SERVICENODE_REST_PORT}" +export STORE_NODE_REST_ADDRESS="http://127.0.0.1:${SERVICENODE_HTTP_PORT}" -# ========================= # Phase 0 — bring up simulator -# ========================= echo "[${TEST_NAME}] Phase 0: bring up simulator" cd ./waku-simulator docker compose up -d @@ -76,24 +69,24 @@ docker run -d --rm --name lpt_cpu \ --network host ${LPT_IMAGE} # ========================= -# Phase 2 — Sonda (exact style from your scripts) -# build image + write env + run with --env-file +# Phase 2 # ========================= echo "[${TEST_NAME}] Phase 2: build and start Sonda" -cd ../sonda +cd ./sonda docker build -t local-perf-sonda -f ./Dockerfile.sonda . cat < perf-test.env -RELAY_NODE_REST_ADDRESS=http://127.0.0.1:8645 -STORE_NODE_REST_ADDRESS=http://127.0.0.1:8644 +RELAY_NODE_REST_ADDRESS=${RELAY_NODE_REST_ADDRESS} +STORE_NODE_REST_ADDRESS=${STORE_NODE_REST_ADDRESS} QUERY_DELAY=0.5 STORE_NODES=/ip4/10.2.0.101/tcp/60001/p2p/16Uiu2HAkyte8uj451tGkbww4Mjcg6DRnmAHxNeWyF4zp23RbpG3n -CLUSTER_ID=66 -SHARD=0 +CLUSTER_ID=${CLUSTER_ID} +SHARD=${SHARD} +PUBSUB_TOPIC=/waku/2/rs/${CLUSTER_ID}/${SHARD} EOF docker run --env-file perf-test.env -l sonda -d --network host local-perf-sonda -cd ../waku-lite/waku-protocol-perf-test +cd .. echo "[${TEST_NAME}] hold 12m" sleep 720 diff --git a/scripts/Lite_protocol_scripts/store/store_scenario6.sh b/scripts/Lite_protocol_scripts/store/store_scenario_6.sh similarity index 90% rename from scripts/Lite_protocol_scripts/store/store_scenario6.sh rename to scripts/Lite_protocol_scripts/store/store_scenario_6.sh index c193fa20..d80ca99b 100755 --- a/scripts/Lite_protocol_scripts/store/store_scenario6.sh +++ b/scripts/Lite_protocol_scripts/store/store_scenario_6.sh @@ -9,10 +9,10 @@ echo "[${TEST_NAME}] start" # Images (pin for comparability) export NWAKU_IMAGE="wakuorg/nwaku:stable" export LPT_IMAGE="wakuorg/liteprotocoltester:latest" -export SONDA_IMAGE="wakuorg/sonda:latest" # change if you host it elsewhere +export SONDA_IMAGE="wakuorg/sonda:latest" # Service/DB resources (CPU bottleneck on service) -export SERVICENODE_CPU_CORES=0 # single core to amplify CPU load +export SERVICENODE_CPU_CORES=0 export POSTGRES_CPU_CORES=1-3 export SERVICE_MEM_LIMIT="2g" export POSTGRES_MEM_LIMIT="2g" @@ -43,12 +43,12 @@ sleep 30 # ---------- Phase 1: CPU write hammer (small msgs, high rate) ---------- # Small payloads + high publisher count => per-message CPU (encode/verify/route) dominates. export NUM_PUBLISHER_NODES=24 -export NUM_RECEIVER_NODES=8 # keep some receivers to drive end-to-end -export MESSAGE_INTERVAL_MILLIS=8 # ~125 msg/s per publisher (adjust if too hot) -export MIN_MESSAGE_SIZE=256 # bytes -export MAX_MESSAGE_SIZE=1024 # bytes +export NUM_RECEIVER_NODES=8 +export MESSAGE_INTERVAL_MILLIS=8 +export MIN_MESSAGE_SIZE=256 +export MAX_MESSAGE_SIZE=1024 export START_PUBLISHING_AFTER=10 -export NUM_MESSAGES=0 # unlimited +export NUM_MESSAGES=0 echo "[${TEST_NAME}] phase1 writers: ${NUM_PUBLISHER_NODES} pubs @ ${MESSAGE_INTERVAL_MILLIS}ms, 256-1024B" docker run -d --rm --name lpt_cpu \ From e532e6a685c787b8568b98d221ce4c2906c5ebd8 Mon Sep 17 00:00:00 2001 From: aya Date: Fri, 26 Sep 2025 16:22:41 +0300 Subject: [PATCH 3/7] Add merged scripts --- .../store/nodes_15_scenario2.sh | 151 +++++++++++++++ .../store/nodes_15_service_1.sh | 21 +- .../store/store_cpu_stress.sh | 4 +- .../stress_high_concurrency_scenario2.sh | 183 ++++++++++++++++++ 4 files changed, 347 insertions(+), 12 deletions(-) create mode 100755 scripts/Lite_protocol_scripts/store/nodes_15_scenario2.sh create mode 100755 scripts/Lite_protocol_scripts/store/stress_high_concurrency_scenario2.sh diff --git a/scripts/Lite_protocol_scripts/store/nodes_15_scenario2.sh b/scripts/Lite_protocol_scripts/store/nodes_15_scenario2.sh new file mode 100755 index 00000000..f4dbb35a --- /dev/null +++ b/scripts/Lite_protocol_scripts/store/nodes_15_scenario2.sh @@ -0,0 +1,151 @@ +#!/bin/bash +set -e +if ! typeset -f wn >/dev/null 2>&1; then + wn() { docker compose "$@"; } +fi +# - waku-simulator: 15-node network, 1 service, 1 edge +# - Service node limited by env below (CPU cores 0-3; memory 512 MiB) +# - Phases driven by LPT; Sonda runs throughout to exercise Store +# - This variant stresses CPU more and uses 120s observe windows + +echo " Running test..." + +# -------------------- Bring up simulator ------------------------ +cd ./waku-simulator + +export NWAKU_IMAGE=wakuorg/nwaku:latest +export NUM_NWAKU_NODES=15 +export RLN_ENABLED=false + +export SERVICENODE_CPU_CORES="0-3" +export SERVICENODE_MEM_LIMIT=512m +export POSTGRES_CPU_CORES="0-3" +export POSTGRES_MEM_LIMIT=2g +export POSTGRES_SHM=1g + +docker compose up -d + +# Wait until service node is running +while true; do + sid="$(docker compose ps -q servicenode || true)" + if [[ -n "$sid" ]]; then + state="$(docker inspect --format '{{.State.Status}}' "$sid" 2>/dev/null || true)" + [[ "$state" == "running" ]] && break + fi + sleep 1 +done + +cd ../lpt + +# -------------------- LPT common knobs (same exports) ------------------------- +export LPT_IMAGE=harbor.status.im/wakuorg/liteprotocoltester:latest +export START_PUBLISHING_AFTER=15 +export NUM_MESSAGES=0 +export MESSAGE_INTERVAL_MILLIS=100 + +export MIN_MESSAGE_SIZE=120Kb +export MAX_MESSAGE_SIZE=145Kb + +export LIGHTPUSH_SERVICE_PEER=/ip4/10.2.0.101/tcp/60001/p2p/16Uiu2HAkyte8uj451tGkbww4Mjcg6DRnmAHxNeWyF4zp23RbpG3n +export FILTER_SERVICE_PEER=/ip4/10.2.0.101/tcp/60001/p2p/16Uiu2HAkyte8uj451tGkbww4Mjcg6DRnmAHxNeWyF4zp23RbpG3n + +export PUBSUB=/waku/2/rs/66/0 +export CONTENT_TOPIC=/tester/2/light-pubsub-test/wakusim +export CLUSTER_ID=66 + +# wait time before starting traffic +sleep 60 + + +# -------------------- Sonda (Store monitor) ----------------------------------- +cd ../sonda + +docker build -t local-perf-sonda -f ./Dockerfile.sonda . + +# perf-test.env +cat < perf-test.env +RELAY_NODE_REST_ADDRESS=http://127.0.0.1:8645 +STORE_NODE_REST_ADDRESS=http://127.0.0.1:8644 +QUERY_DELAY=0.5 +STORE_NODES=/ip4/10.2.0.101/tcp/60001/p2p/16Uiu2HAkyte8uj451tGkbww4Mjcg6DRnmAHxNeWyF4zp23RbpG3n +CLUSTER_ID=66 +SHARD=0 +EOF + +sleep 5 + +docker rm -f sonda >/dev/null 2>&1 || true +docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda + +cd ../lpt + +# -------------------- Phase 1: 6 pub / 6 recv -------------------------------- +export NUM_PUBLISHER_NODES=6 +export NUM_RECEIVER_NODES=6 +docker compose down -v >/dev/null 2>&1 || true +docker compose up -d + +current_time=$(date +"%Y-%m-%d %H:%M:%S") +echo "[test] LPT is running with 6 publishers and 6 receivers + sonda from now: $current_time" + +sleep 120 + +# -------------------- Phase 2: 3 pub / 12 recv ------------------------------- +docker compose down -v +export NUM_PUBLISHER_NODES=3 +export NUM_RECEIVER_NODES=12 +docker compose up -d + +current_time=$(date +"%Y-%m-%d %H:%M:%S") +echo "[test] LPT is running with 3 publishers and 12 receivers from now: $current_time" + +sleep 120 + +# -------------------- Phase 3: 12 pub / 3 recv ------------------------------- +docker compose down -v +export NUM_PUBLISHER_NODES=12 +export NUM_RECEIVER_NODES=3 +docker compose up -d + +current_time=$(date +"%Y-%m-%d %H:%M:%S") +echo "[test] LPT is running with 12 publishers and 3 receivers from now: $current_time" + +sleep 120 + +# -------------------- Phase 4: receivers down; keep publisher + sonda -------- +docker compose down -v +export NUM_PUBLISHER_NODES=12 +export NUM_RECEIVER_NODES=0 +docker compose up -d + +current_time=$(date +"%Y-%m-%d %H:%M:%S") +echo "[test] LPT receivers are down; sonda and lightpush publisher running from now: $current_time" + +sleep 120 + +# -------------------- Phase 5: LPT down; only sonda -------------------------- +docker compose down -v + +current_time=$(date +"%Y-%m-%d %H:%M:%S") +echo "[test] LPT down; only sonda is working from now: $current_time" + +sleep 120 + +# -------------------- Phase 6: final high-load burst ----------------- + +export NUM_PUBLISHER_NODES=12 +export NUM_RECEIVER_NODES=12 +docker compose up -d + +current_time=$(date +"%Y-%m-%d %H:%M:%S") +echo "[test] Final burst: LPT running with 12 publishers and 12 receivers from now: $current_time" + +sleep 120 + +cd .. + +current_time=$(date +"%Y-%m-%d %H:%M:%S") +echo "[test] Test finished at $current_time" + +# finish +# exec ./stop_test.sh diff --git a/scripts/Lite_protocol_scripts/store/nodes_15_service_1.sh b/scripts/Lite_protocol_scripts/store/nodes_15_service_1.sh index 5d6ce322..60b30633 100755 --- a/scripts/Lite_protocol_scripts/store/nodes_15_service_1.sh +++ b/scripts/Lite_protocol_scripts/store/nodes_15_service_1.sh @@ -6,9 +6,9 @@ set -e # - Phases driven by LPT; Sonda runs throughout to exercise Store # - This variant stresses CPU more and uses 120s observe windows -echo "[store_scenario1] Running test..." +echo "Running test..." -# -------------------- Bring up simulator (same exports) ------------------------ +# -------------------- Bring up simulator ------------------------ cd ./waku-simulator export NWAKU_IMAGE=wakuorg/nwaku:latest @@ -35,7 +35,7 @@ done cd ../lpt -# -------------------- LPT common knobs (same exports) ------------------------- +# -------------------- LPT common knobs ------------------------- export LPT_IMAGE=harbor.status.im/wakuorg/liteprotocoltester:latest export START_PUBLISHING_AFTER=15 export NUM_MESSAGES=0 @@ -61,7 +61,7 @@ cd ../sonda docker build -t local-perf-sonda -f ./Dockerfile.sonda . -# perf-test.env (same style as scenario_1.sh) +# perf-test.env cat < perf-test.env RELAY_NODE_REST_ADDRESS=http://127.0.0.1:8645 STORE_NODE_REST_ADDRESS=http://127.0.0.1:8644 @@ -85,7 +85,7 @@ docker compose down -v >/dev/null 2>&1 || true docker compose up -d current_time=$(date +"%Y-%m-%d %H:%M:%S") -echo "[store_scenario1] LPT is running with 6 publishers and 6 receivers + sonda from now: $current_time" +echo "LPT is running with 6 publishers and 6 receivers + sonda from now: $current_time" sleep 120 @@ -96,7 +96,7 @@ export NUM_RECEIVER_NODES=12 docker compose up -d current_time=$(date +"%Y-%m-%d %H:%M:%S") -echo "[store_scenario1] LPT is running with 3 publishers and 12 receivers from now: $current_time" +echo "LPT is running with 3 publishers and 12 receivers from now: $current_time" sleep 120 @@ -107,7 +107,7 @@ export NUM_RECEIVER_NODES=3 docker compose up -d current_time=$(date +"%Y-%m-%d %H:%M:%S") -echo "[store_scenario1] LPT is running with 12 publishers and 3 receivers from now: $current_time" +echo "LPT is running with 12 publishers and 3 receivers from now: $current_time" sleep 120 @@ -118,7 +118,7 @@ export NUM_RECEIVER_NODES=0 docker compose up -d current_time=$(date +"%Y-%m-%d %H:%M:%S") -echo "[store_scenario1] LPT receivers are down; sonda and lightpush publisher running from now: $current_time" +echo "LPT receivers are down; sonda and lightpush publisher running from now: $current_time" sleep 120 @@ -126,14 +126,15 @@ sleep 120 docker compose down -v current_time=$(date +"%Y-%m-%d %H:%M:%S") -echo "[store_scenario1] LPT down; only sonda is working from now: $current_time" +echo "LPT down; only sonda is working from now: $current_time" sleep 120 cd .. current_time=$(date +"%Y-%m-%d %H:%M:%S") -echo "[store_scenario1] Test finished at $current_time" +echo "Test finished at $current_time" # finish # exec ./stop_test.sh +s \ No newline at end of file diff --git a/scripts/Lite_protocol_scripts/store/store_cpu_stress.sh b/scripts/Lite_protocol_scripts/store/store_cpu_stress.sh index ecd0546e..299a9263 100755 --- a/scripts/Lite_protocol_scripts/store/store_cpu_stress.sh +++ b/scripts/Lite_protocol_scripts/store/store_cpu_stress.sh @@ -88,8 +88,8 @@ EOF docker run --env-file perf-test.env -l sonda -d --network host local-perf-sonda cd .. -echo "[${TEST_NAME}] hold 12m" -sleep 720 +echo "[${TEST_NAME}] hold 240s" +sleep 240 # ========================= # Phase 3 — recovery diff --git a/scripts/Lite_protocol_scripts/store/stress_high_concurrency_scenario2.sh b/scripts/Lite_protocol_scripts/store/stress_high_concurrency_scenario2.sh new file mode 100755 index 00000000..760dc400 --- /dev/null +++ b/scripts/Lite_protocol_scripts/store/stress_high_concurrency_scenario2.sh @@ -0,0 +1,183 @@ +#!/bin/bash +set -e + +# Heavier Store stress by stacking more concurrent Sonda readers +# and ramping LPT publishers/receivers & message sizes. + +cd ./waku-simulator + +export NWAKU_IMAGE=wakuorg/nwaku:latest +export NUM_NWAKU_NODES=15 +export RLN_ENABLED=false + +# Service node config +export SERVICENODE_CPU_CORES="0-3" +export SERVICENODE_MEM_LIMIT=2g +export POSTGRES_CPU_CORES="0-3" +export POSTGRES_MEM_LIMIT=2g +export POSTGRES_SHM=1g + +docker compose up -d + +# Wait until service node is running +while true; do + sid="$(docker compose ps -q servicenode || true)" + if [[ -n "$sid" ]]; then + state="$(docker inspect --format '{{.State.Status}}' "$sid" 2>/dev/null || true)" + [[ "$state" == "running" ]] && break + fi + sleep 1 +done + +cd ../lpt + +# -------------------- LPT config --------------------------- +export LPT_IMAGE=harbor.status.im/wakuorg/liteprotocoltester:latest +export START_PUBLISHING_AFTER=15 +export NUM_MESSAGES=0 +export MESSAGE_INTERVAL_MILLIS=100 + +# Service peers +export LIGHTPUSH_SERVICE_PEER=/ip4/10.2.0.101/tcp/60001/p2p/16Uiu2HAkyte8uj451tGkbww4Mjcg6DRnmAHxNeWyF4zp23RbpG3n +export FILTER_SERVICE_PEER=/ip4/10.2.0.101/tcp/60001/p2p/16Uiu2HAkyte8uj451tGkbww4Mjcg6DRnmAHxNeWyF4zp23RbpG3n + +# Topics +export PUBSUB=/waku/2/rs/66/0 +export CONTENT_TOPIC=/tester/2/light-pubsub-test/wakusim +export CLUSTER_ID=66 + +# settle before traffic +sleep 60 + +cd ../sonda +docker build -t local-perf-sonda -f ./Dockerfile.sonda . + +# Keep perf-test.env location and content +cat < perf-test.env +RELAY_NODE_REST_ADDRESS=http://127.0.0.1:8645 +STORE_NODE_REST_ADDRESS=http://127.0.0.1:8644 +QUERY_DELAY=0.5 +STORE_NODES=/ip4/10.2.0.101/tcp/60001/p2p/16Uiu2HAkyte8uj451tGkbww4Mjcg6DRnmAHxNeWyF4zp23RbpG3n +CLUSTER_ID=66 +SHARD=0 +EOF + +sleep 5 + +# Clean old sonda +docker rm -f sonda1 sonda2 sonda3 sonda4 sonda5 sonda6 sonda7 sonda8 sonda9 sonda10 >/dev/null 2>&1 || true + +# Baseline Sonda (0.5s) +docker run -d --name sonda1 --network host -l sonda \ + --env-file ./perf-test.env local-perf-sonda + +cd ../lpt + +# ============================================================================ # +# PHASE 0: Warmup, small messages, quick history fill +# LPT 8/8, 20–40 KB; Sonda x1 @0.5s +# ============================================================================ # +export NUM_PUBLISHER_NODES=8 +export NUM_RECEIVER_NODES=8 +export MIN_MESSAGE_SIZE=20Kb +export MAX_MESSAGE_SIZE=40Kb + +docker compose down -v >/dev/null 2>&1 || true +docker compose up -d + +echo "Phase 0: LPT 8/8, 20–40KB; Sonda x1 @0.5s — $(date '+%F %T')" +sleep 30 + +# ============================================================================ # +# PHASE 1: Moderate traffic +# LPT 6/6, 50–90 KB; Sonda x1 @0.5s +# ============================================================================ # +export NUM_PUBLISHER_NODES=6 +export NUM_RECEIVER_NODES=6 +export MIN_MESSAGE_SIZE=50Kb +export MAX_MESSAGE_SIZE=90Kb + +docker compose down -v >/dev/null 2>&1 || true +docker compose up -d + +echo "Phase 1: LPT 6/6, 50–90KB; Sonda x1 @0.5s — $(date '+%F %T')" +sleep 30 + +# ============================================================================ # +# PHASE 2: Triple query rate +# Keep LPT; add Sonda x2 @0.1s (total 3) +# ============================================================================ # +docker run -d --name sonda2 --network host -l sonda \ + --env-file ../sonda/perf-test.env -e QUERY_DELAY=0.1 local-perf-sonda +docker run -d --name sonda3 --network host -l sonda \ + --env-file ../sonda/perf-test.env -e QUERY_DELAY=0.1 local-perf-sonda + +echo "Phase 2: +Sonda x2 @0.1s (total 3) — $(date '+%F %T')" +sleep 30 + +# ============================================================================ # +# PHASE 3: Heavier traffic + peak query rate +# LPT 10/10, 130–149 KB; +Sonda x2 @0.05s (total 5) +# ============================================================================ # +docker compose down -v +export NUM_PUBLISHER_NODES=10 +export NUM_RECEIVER_NODES=10 +export MIN_MESSAGE_SIZE=130Kb +export MAX_MESSAGE_SIZE=149Kb +docker compose up -d + +docker run -d --name sonda4 --network host -l sonda \ + --env-file ../sonda/perf-test.env -e QUERY_DELAY=0.05 local-perf-sonda +docker run -d --name sonda5 --network host -l sonda \ + --env-file ../sonda/perf-test.env -e QUERY_DELAY=0.05 local-perf-sonda + +echo "Phase 3: LPT 10/10, 130–149KB; Sonda total x5 — $(date '+%F %T')" +sleep 30 + +# ============================================================================ # +# PHASE 3.5: Max Store pressure spike (Store-heavy while LPT still running) +# Add Sonda x3 @0.02s (total 8) +# ============================================================================ # +docker run -d --name sonda6 --network host -l sonda \ + --env-file ../sonda/perf-test.env -e QUERY_DELAY=0.02 local-perf-sonda +docker run -d --name sonda7 --network host -l sonda \ + --env-file ../sonda/perf-test.env -e QUERY_DELAY=0.02 local-perf-sonda +docker run -d --name sonda8 --network host -l sonda \ + --env-file ../sonda/perf-test.env -e QUERY_DELAY=0.02 local-perf-sonda + +echo "Phase 3.5: +Sonda x3 @0.02s (total 8) — $(date '+%F %T')" +sleep 30 + +# ============================================================================ # +# PHASE 4: Store-only flood +# Stop LPT; observe pure Store query saturation +# ============================================================================ # +docker compose down -v + +echo "Phase 4: LPT down; Store-only with Sonda x8 — $(date '+%F %T')" +sleep 30 + +# ============================================================================ # +# PHASE 5: Final squeeze +# Add Sonda x2 more @0.01s (total 10) for short burst +# ============================================================================ # +docker run -d --name sonda9 --network host -l sonda \ + --env-file ../sonda/perf-test.env -e QUERY_DELAY=0.01 local-perf-sonda +docker run -d --name sonda10 --network host -l sonda \ + --env-file ../sonda/perf-test.env -e QUERY_DELAY=0.01 local-perf-sonda + +echo "Phase 5: +Sonda x2 @0.01s (total 10) — $(date '+%F %T')" +sleep 30 + +# brief post-run observation +echo "Final observe window — $(date '+%F %T')" +sleep 30 + +# -------------------- Cleanup ------------------------------------------------- +docker rm -f sonda1 sonda2 sonda3 sonda4 sonda5 sonda6 sonda7 sonda8 sonda9 sonda10 >/dev/null 2>&1 || true + +cd .. + +echo "Test finished at $(date '+%F %T')" +# exec ./stop_test.sh + From f53809ce0a87bc28279403e3d36740c580393b85 Mon Sep 17 00:00:00 2001 From: aya Date: Fri, 26 Sep 2025 16:38:19 +0300 Subject: [PATCH 4/7] last test script added --- ...tress_alternate_high_concurrency_scenario3 | 208 ++++++++++++++++++ 1 file changed, 208 insertions(+) create mode 100755 scripts/Lite_protocol_scripts/store/stress_alternate_high_concurrency_scenario3 diff --git a/scripts/Lite_protocol_scripts/store/stress_alternate_high_concurrency_scenario3 b/scripts/Lite_protocol_scripts/store/stress_alternate_high_concurrency_scenario3 new file mode 100755 index 00000000..51712c90 --- /dev/null +++ b/scripts/Lite_protocol_scripts/store/stress_alternate_high_concurrency_scenario3 @@ -0,0 +1,208 @@ +#!/bin/bash +set -e + +cd ./waku-simulator + +export NWAKU_IMAGE=wakuorg/nwaku:latest +export NUM_NWAKU_NODES=15 +export RLN_ENABLED=false + +# Service node config +export SERVICENODE_CPU_CORES="0-3" +export SERVICENODE_MEM_LIMIT=2g +export POSTGRES_CPU_CORES="0-3" +export POSTGRES_MEM_LIMIT=2g +export POSTGRES_SHM=1g + +docker compose up -d + +# Wait until service node is running +while true; do + sid="$(docker compose ps -q servicenode || true)" + if [[ -n "$sid" ]]; then + state="$(docker inspect --format '{{.State.Status}}' "$sid" 2>/dev/null || true)" + [[ "$state" == "running" ]] && break + fi + sleep 1 +done + +cd ../lpt + +# -------------------- LPT config --------------------------- +export LPT_IMAGE=harbor.status.im/wakuorg/liteprotocoltester:latest +export START_PUBLISHING_AFTER=15 +export NUM_MESSAGES=0 +export MESSAGE_INTERVAL_MILLIS=100 + +# Service peers +export LIGHTPUSH_SERVICE_PEER=/ip4/10.2.0.101/tcp/60001/p2p/16Uiu2HAkyte8uj451tGkbww4Mjcg6DRnmAHxNeWyF4zp23RbpG3n +export FILTER_SERVICE_PEER=/ip4/10.2.0.101/tcp/60001/p2p/16Uiu2HAkyte8uj451tGkbww4Mjcg6DRnmAHxNeWyF4zp23RbpG3n + +# Topics +export PUBSUB=/waku/2/rs/66/0 +export CONTENT_TOPIC=/tester/2/light-pubsub-test/wakusim +export CLUSTER_ID=66 + +# Allow services to settle +sleep 60 + +cd ../sonda +docker build -t local-perf-sonda -f ./Dockerfile.sonda . + +cat < perf-test.env +RELAY_NODE_REST_ADDRESS=http://127.0.0.1:8645 +STORE_NODE_REST_ADDRESS=http://127.0.0.1:8644 +QUERY_DELAY=0.5 +STORE_NODES=/ip4/10.2.0.101/tcp/60001/p2p/16Uiu2HAkyte8uj451tGkbww4Mjcg6DRnmAHxNeWyF4zp23RbpG3n +CLUSTER_ID=66 +SHARD=0 +EOF + +sleep 5 + +# Clean old +docker rm -f sonda1 sonda2 sonda3 sonda4 sonda5 sonda6 sonda7 sonda8 sonda9 sonda10 sonda11 sonda12 >/dev/null 2>&1 || true + +# Baseline Sonda (0.5s) +docker run -d --name sonda1 --network host -l sonda \ + --env-file ./perf-test.env local-perf-sonda + +cd ../lpt + +# Helper echo +ts() { date '+%F %T'; } + +# ============================================================================ # +# PHASE A: Wave burst #1 (short & intense), then cooldown +# Idea: quick history fill + heavy query spike +# ============================================================================ # +export NUM_PUBLISHER_NODES=8 +export NUM_RECEIVER_NODES=8 +export MIN_MESSAGE_SIZE=80Kb +export MAX_MESSAGE_SIZE=120Kb + +docker compose down -v >/dev/null 2>&1 || true +docker compose up -d +echo " Phase A1: LPT 8/8, 80–120KB; Sonda x1 @0.5s — $(ts)" + +# Add two fast Sonda for burst @0.05s +docker run -d --name sonda2 --network host -l sonda \ + --env-file ../sonda/perf-test.env -e QUERY_DELAY=0.05 local-perf-sonda +docker run -d --name sonda3 --network host -l sonda \ + --env-file ../sonda/perf-test.env -e QUERY_DELAY=0.05 local-perf-sonda + +sleep 90 + +# Cooldown: remove the two fast ones +docker rm -f sonda2 sonda3 >/dev/null 2>&1 || true +echo "Phase A2: cooldown to Sonda x1 @0.5s — $(ts)" +sleep 60 + +# ============================================================================ # +# PHASE B: Wave burst #2 (bigger messages, more pubs/recvs), then cooldown +# ============================================================================ # +export NUM_PUBLISHER_NODES=10 +export NUM_RECEIVER_NODES=10 +export MIN_MESSAGE_SIZE=120Kb +export MAX_MESSAGE_SIZE=160Kb + +docker compose down -v +docker compose up -d +echo " Phase B1: LPT 10/10, 120–160KB; Sonda x1 — $(ts)" + +# Heavier spike: three Sonda @0.02s +docker run -d --name sonda4 --network host -l sonda \ + --env-file ../sonda/perf-test.env -e QUERY_DELAY=0.02 local-perf-sonda +docker run -d --name sonda5 --network host -l sonda \ + --env-file ../sonda/perf-test.env -e QUERY_DELAY=0.02 local-perf-sonda +docker run -d --name sonda6 --network host -l sonda \ + --env-file ../sonda/perf-test.env -e QUERY_DELAY=0.02 local-perf-sonda + +sleep 120 + +# Cooldown again: keep only baseline +docker rm -f sonda4 sonda5 sonda6 >/dev/null 2>&1 || true +echo "[scenario_7_store_wave] Phase B2: cooldown to Sonda x1 @0.5s — $(ts)" +sleep 60 + +# ============================================================================ # +# PHASE C: Microburst trains (while LPT steady) +# Start/stop pairs rapidly to create sawtooth Store pressure. +# ============================================================================ # +export NUM_PUBLISHER_NODES=10 +export NUM_RECEIVER_NODES=10 +export MIN_MESSAGE_SIZE=60Kb +export MAX_MESSAGE_SIZE=100Kb + +docker compose down -v +docker compose up -d +echo " Phase C: microburst trains begin — $(ts)" + +# Train 1 (two @0.01s for 45s) +docker run -d --name sonda7 --network host -l sonda \ + --env-file ../sonda/perf-test.env -e QUERY_DELAY=0.01 local-perf-sonda +docker run -d --name sonda8 --network host -l sonda \ + --env-file ../sonda/perf-test.env -e QUERY_DELAY=0.01 local-perf-sonda +sleep 45 +docker rm -f sonda7 sonda8 >/dev/null 2>&1 || true + +# Train 2 (two @0.02s for 60s) +docker run -d --name sonda9 --network host -l sonda \ + --env-file ../sonda/perf-test.env -e QUERY_DELAY=0.02 local-perf-sonda +docker run -d --name sonda10 --network host -l sonda \ + --env-file ../sonda/perf-test.env -e QUERY_DELAY=0.02 local-perf-sonda +sleep 60 +docker rm -f sonda9 sonda10 >/dev/null 2>&1 || true + +# Train 3 (three @0.01s for 60s) +docker run -d --name sonda11 --network host -l sonda \ + --env-file ../sonda/perf-test.env -e QUERY_DELAY=0.01 local-perf-sonda +docker run -d --name sonda12 --network host -l sonda \ + --env-file ../sonda/perf-test.env -e QUERY_DELAY=0.01 local-perf-sonda +docker run -d --name sonda3 --network host -l sonda \ + --env-file ../sonda/perf-test.env -e QUERY_DELAY=0.01 local-perf-sonda +sleep 60 +docker rm -f sonda11 sonda12 sonda3 >/dev/null 2>&1 || true + +echo "[scenario_7_store_wave] Phase C: microburst trains done — $(ts)" +sleep 30 + +# ============================================================================ # +# PHASE D: Store-only oscillation (LPT down), cycling query delays +# Observe Store under alternating light/heavy read pressure. +# ============================================================================ # +docker compose down -v +echo "Phase D: Store-only oscillation — $(ts)" + +# Round 1: fast pair @0.02s +docker run -d --name sonda4 --network host -l sonda \ + --env-file ../sonda/perf-test.env -e QUERY_DELAY=0.02 local-perf-sonda +docker run -d --name sonda5 --network host -l sonda \ + --env-file ../sonda/perf-test.env -e QUERY_DELAY=0.02 local-perf-sonda +sleep 60 +docker rm -f sonda4 sonda5 >/dev/null 2>&1 || true + +# Round 2: very fast pair @0.01s +docker run -d --name sonda6 --network host -l sonda \ + --env-file ../sonda/perf-test.env -e QUERY_DELAY=0.01 local-perf-sonda +docker run -d --name sonda7 --network host -l sonda \ + --env-file ../sonda/perf-test.env -e QUERY_DELAY=0.01 local-perf-sonda +sleep 60 +docker rm -f sonda6 sonda7 >/dev/null 2>&1 || true + +# Round 3: light @0.1s for recovery view +docker run -d --name sonda8 --network host -l sonda \ + --env-file ../sonda/perf-test.env -e QUERY_DELAY=0.1 local-perf-sonda +sleep 60 +docker rm -f sonda8 >/dev/null 2>&1 || true + +echo "Final observe window — $(ts)" +sleep 60 + +# -------------------- Cleanup ------------------------------------------------- +docker rm -f sonda1 sonda2 sonda3 sonda4 sonda5 sonda6 sonda7 sonda8 sonda9 sonda10 sonda11 sonda12 >/dev/null 2>&1 || true + +cd .. + +echo "Test finished at $(ts)" +# exec ./stop_test.sh From 0abb67f68b267b6c655ce4b41e33866025cb60c5 Mon Sep 17 00:00:00 2001 From: aya Date: Mon, 29 Sep 2025 22:44:34 +0300 Subject: [PATCH 5/7] Fix review point --- scripts/Lite_protocol_scripts/store/nodes_15_scenario2.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/Lite_protocol_scripts/store/nodes_15_scenario2.sh b/scripts/Lite_protocol_scripts/store/nodes_15_scenario2.sh index f4dbb35a..c3483ac5 100755 --- a/scripts/Lite_protocol_scripts/store/nodes_15_scenario2.sh +++ b/scripts/Lite_protocol_scripts/store/nodes_15_scenario2.sh @@ -18,7 +18,7 @@ export NUM_NWAKU_NODES=15 export RLN_ENABLED=false export SERVICENODE_CPU_CORES="0-3" -export SERVICENODE_MEM_LIMIT=512m +export SERVICENODE_MEM_LIMIT=2g export POSTGRES_CPU_CORES="0-3" export POSTGRES_MEM_LIMIT=2g export POSTGRES_SHM=1g From 159d04f5b0b262a2f85d9802aab02b15dc4e48c7 Mon Sep 17 00:00:00 2001 From: aya Date: Wed, 1 Oct 2025 16:49:20 +0300 Subject: [PATCH 6/7] Adding new test scenarios after review points --- .../Screenshot from 2025-10-01 15-26-55.png | Bin 0 -> 59162 bytes .../Screenshot from 2025-10-01 15-27-31.png | Bin 0 -> 14454 bytes .../Screenshot from 2025-10-01 15-28-14.png | Bin 0 -> 38134 bytes .../store/store_KPI_simple.sh | 121 ++++++++++++++++++ 4 files changed, 121 insertions(+) create mode 100644 scripts/Lite_protocol_scripts/store/reports/store_kpi_simple/Screenshot from 2025-10-01 15-26-55.png create mode 100644 scripts/Lite_protocol_scripts/store/reports/store_kpi_simple/Screenshot from 2025-10-01 15-27-31.png create mode 100644 scripts/Lite_protocol_scripts/store/reports/store_kpi_simple/Screenshot from 2025-10-01 15-28-14.png create mode 100755 scripts/Lite_protocol_scripts/store/store_KPI_simple.sh diff --git a/scripts/Lite_protocol_scripts/store/reports/store_kpi_simple/Screenshot from 2025-10-01 15-26-55.png b/scripts/Lite_protocol_scripts/store/reports/store_kpi_simple/Screenshot from 2025-10-01 15-26-55.png new file mode 100644 index 0000000000000000000000000000000000000000..ad54f686725af5ad95d7c29afd701bf8d1893c23 GIT binary patch literal 59162 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%0Ay$rDd5M6}0xKs`Q2(kIsZAL@ZkU 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

J1xw`M_1RZ6iBd^vf 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 SVH^A3e+sgyGG$Vxf&UNv?oAQ^ literal 0 HcmV?d00001 diff --git a/scripts/Lite_protocol_scripts/store/reports/store_kpi_simple/Screenshot from 2025-10-01 15-28-14.png b/scripts/Lite_protocol_scripts/store/reports/store_kpi_simple/Screenshot from 2025-10-01 15-28-14.png new file mode 100644 index 0000000000000000000000000000000000000000..eb7a6e19904945b1a21f63bd34c26f08a5565765 GIT binary patch literal 38134 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/dev/null || true)" + [[ "$state" == "running" ]] && break + fi + sleep 1 +done + +cd .. + +# -------- Shared exports -------- +export RELAY_NODE_REST_ADDRESS=http://127.0.0.1:8645 +export STORE_NODE_REST_ADDRESS=http://127.0.0.1:8644 +export STORE_NODES=/ip4/10.2.0.101/tcp/60001/p2p/16Uiu2HAkyte8uj451tGkbww4Mjcg6DRnmAHxNeWyF4zp23RbpG3n +export QUERY_DELAY=0.25 +export CLUSTER_ID=66 +export SHARD=0 +export PUBSUB=/waku/2/rs/66/0 +export CONTENT_TOPIC=/tester/2/light-pubsub-test/wakusim + +cd ./lpt + +export LPT_IMAGE=harbor.status.im/wakuorg/liteprotocoltester:latest + +export NUM_PUBLISHER_NODES=5 +export NUM_RECEIVER_NODES=5 +export START_PUBLISHING_AFTER=15 +export NUM_MESSAGES=0 +export MESSAGE_INTERVAL_MILLIS=100 +export MIN_MESSAGE_SIZE=120Kb +export MAX_MESSAGE_SIZE=145Kb + + +export LIGHTPUSH_SERVICE_PEER="$STORE_NODES" +export FILTER_SERVICE_PEER="$STORE_NODES" +export PUBSUB +export CONTENT_TOPIC +export CLUSTER_ID + +docker compose up -d + +cd .. + +cd ./sonda +docker build -t local-perf-sonda -f ./Dockerfile.sonda . + +cat < perf-test.env +RELAY_NODE_REST_ADDRESS=${RELAY_NODE_REST_ADDRESS} +STORE_NODE_REST_ADDRESS=${STORE_NODE_REST_ADDRESS} +QUERY_DELAY=${QUERY_DELAY} +STORE_NODES=${STORE_NODES} +CLUSTER_ID=${CLUSTER_ID} +SHARD=${SHARD} +EOF + +sleep 5 +docker rm -f sonda >/dev/null 2>&1 || true +docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda +cd .. + +echo "[store_kpi] warmup 60s to build history…" +sleep 60 + +# -------- Store KPI (10-minute query loop, in parallel with LPT) -------- +URL="${STORE_NODE_REST_ADDRESS}/store/v3/messages" +PEERADDR="${STORE_NODES%%,*}" + +burst_once() { + local now start par + now=$(( $(date +%s) * 1000 )) + start=$(( now - 600 * 1000 )) # 10-minute window + par=20 + echo "[burst] 10m window, ${par} parallel → ${URL}" + for i in $(seq 1 $par); do + curl -s --get "$URL" \ + --data-urlencode "peerAddr=$PEERADDR" \ + --data-urlencode "pubsubTopic=$PUBSUB" \ + --data-urlencode "contentTopics=$CONTENT_TOPIC" \ + --data-urlencode "includeData=true" \ + --data-urlencode "startTime=$start" \ + > /dev/null & + done + wait +} + +RUN_MINUTES=10 +END_TS=$(( $(date +%s) + RUN_MINUTES*60 )) +iter=0 +echo "[store_kpi] querying for ${RUN_MINUTES} minutes while LPT publishes…" +while (( $(date +%s) < END_TS )); do + iter=$((iter+1)) + burst_once + echo "[burst] iter=$iter done; sleeping 10s" + sleep 10 +done + +# -------- Tidy -------- +echo "[store_kpi] 10min run complete. Stopping LPT stack…" +( cd ./lpt && docker compose down ) From 61a7a0c81b33f4cdca9ad8ae28645c8aedb7bacb Mon Sep 17 00:00:00 2001 From: aya Date: Wed, 1 Oct 2025 23:36:07 +0300 Subject: [PATCH 7/7] Adding the second scenario after review comments --- .../store/store_kpi_small_page.sh | 92 +++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100755 scripts/Lite_protocol_scripts/store/store_kpi_small_page.sh diff --git a/scripts/Lite_protocol_scripts/store/store_kpi_small_page.sh b/scripts/Lite_protocol_scripts/store/store_kpi_small_page.sh new file mode 100755 index 00000000..fbc16d74 --- /dev/null +++ b/scripts/Lite_protocol_scripts/store/store_kpi_small_page.sh @@ -0,0 +1,92 @@ +#!/bin/bash +set -e + + + +cd ./waku-simulator + +export NWAKU_IMAGE=wakuorg/nwaku:latest +export NUM_NWAKU_NODES=15 +export RLN_ENABLED=false + +export SERVICENODE_CPU_CORES="0-3" +export SERVICENODE_MEM_LIMIT=2g +export POSTGRES_CPU_CORES="0-3" +export POSTGRES_MEM_LIMIT=2g +export POSTGRES_SHM=1g + +docker compose up -d +while true; do + sid="$(docker compose ps -q servicenode || true)" + if [[ -n "$sid" ]]; then + state="$(docker inspect --format '{{.State.Status}}' "$sid" 2>/dev/null || true)" + [[ "$state" == "running" ]] && break + fi + sleep 1 +done + +cd .. + +export RELAY_NODE_REST_ADDRESS=http://127.0.0.1:8645 +export STORE_NODE_REST_ADDRESS=http://127.0.0.1:8644 +export STORE_NODES=/ip4/10.2.0.101/tcp/60001/p2p/16Uiu2HAkyte8uj451tGkbww4Mjcg6DRnmAHxNeWyF4zp23RbpG3n +export QUERY_DELAY=0.5 +export CLUSTER_ID=66 +export SHARD=0 +export PUBSUB=/waku/2/rs/66/0 +export CONTENT_TOPIC=/tester/2/light-pubsub-test/wakusim + +cd ./sonda +docker build -t local-perf-sonda -f ./Dockerfile.sonda . + +cat < perf-test.env +RELAY_NODE_REST_ADDRESS=${RELAY_NODE_REST_ADDRESS} +STORE_NODE_REST_ADDRESS=${STORE_NODE_REST_ADDRESS} +QUERY_DELAY=${QUERY_DELAY} +STORE_NODES=${STORE_NODES} +CLUSTER_ID=${CLUSTER_ID} +SHARD=${SHARD} +EOF + +sleep 5 +docker rm -f sonda >/dev/null 2>&1 || true +docker run --env-file ./perf-test.env -l sonda -d --network host local-perf-sonda +cd .. + +echo "[store_kpi_smallpage] warmup 60s to build history…" +sleep 60 + +URL="${STORE_NODE_REST_ADDRESS}/store/v3/messages" +PEERADDR="${STORE_NODES%%,*}" # fixed to first entry + +burst_once() { + local now start + now=$(( $(date +%s) * 1000 )) + start=$(( now - 600 * 1000 )) # 10-minute window + local par=20 + echo "[burst-smallpage] 10m window, ${par} parallel → ${URL}" + for i in $(seq 1 $par); do + curl -s --get "$URL" \ + --data-urlencode "peerAddr=$PEERADDR" \ + --data-urlencode "pubsubTopic=$PUBSUB" \ + --data-urlencode "contentTopics=$CONTENT_TOPIC" \ + --data-urlencode "includeData=true" \ + --data-urlencode "pageSize=5" \ + --data-urlencode "startTime=$start" \ + > /dev/null & + done + wait +} + +# run bursts for a full 10 minutes +RUN_MINUTES=10 +END_TS=$(( $(date +%s) + RUN_MINUTES*60 )) +iter=0 +echo "[store_kpi_smallpage] querying for ${RUN_MINUTES} minutes…" +while (( $(date +%s) < END_TS )); do + iter=$((iter+1)) + burst_once + echo "[burst-smallpage] iter=$iter done; sleeping 10s" + sleep 10 +done +