Files
amethyst/relayBench/run.sh
Claude 5f3a790d56 feat: add relayBench — head-to-head relay benchmark (geode vs strfry vs any)
New :relayBench module that boots relay implementations as real external
processes under equivalent setups (persistent storage, sig verification on,
no auth) and compares them on the same corpus:

- Ingest: receipt->queryable-by-REQ latency (publish on one connection,
  hammer-poll REQ{ids} on another), OK-ack latency, and pipelined corpus
  replay throughput over N connections.
- Queries: client-realistic filters derived from the corpus (home feed,
  thread, notifications, profiles, hashtag, by-ids, ...), time-to-EOSE
  percentiles plus aggregate events/sec under concurrency; result-set
  counts are cross-checked between relays and mismatches flagged.
- NIP-77 negentropy sync between every relay pair: 80%/80% slices with 60%
  overlap, reconcile timing/rounds/wire-bytes per server side, delta
  transfer, steady-state identical-set reconcile, convergence verified.
- Storage footprint after full ingest.

Corpora (all cached as NDJSON + manifest with a sha256 id fingerprint so
results are comparable across runs and machines):
- synthetic (default): deterministic to the byte — seeded keys, fixed
  timestamps, seed-derived BIP-340 aux nonces — with a realistic social
  shape (zipf authors, threads, reactions, reposts, zap request/receipt
  pairs, hashtags);
- the checked-in real dump (quartz test fixture, ~31k unique 2024 events);
- external dumps (NDJSON or JSON array, gzip sniffed by magic bytes),
  e.g. the 2.1M contact-list archive, with --max-event-bytes/--max-tags
  raising both the corpus filter and the strfry config together;
- live download from public relays.

Every source runs through the same preparation: dedup, drop unsigned/
ephemeral/kind-5, enforce relay ingest caps, parallel Schnorr verify,
chronological sort.

relayBench/run.sh is the one-command entry point: builds geode + harness,
resolves strfry (STRFRY_BIN, PATH, or source build into .cache), runs the
suite and renders an ANSI report with per-metric bars and winners, plus
report.md and results.json under relayBench/results/<timestamp>/.

The harness client disables Nagle (TCP_NODELAY): with the JDK default, a
REQ following the previous round's CLOSE stalls a full delayed-ACK
interval and every latency floors at ~44 ms against both geode and strfry
(verified: ~0.3 ms with it off).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NeoCvXnTxsKzqurkmjdC46
2026-07-03 18:37:16 +00:00

99 lines
2.9 KiB
Bash
Executable File

#!/usr/bin/env bash
#
# relaybench — one-command Nostr relay benchmark (geode vs strfry vs anything).
#
# ./relayBench/run.sh # deterministic 10k synthetic corpus
# ./relayBench/run.sh --quick # 2k-event smoke run
# ./relayBench/run.sh --real # replay the checked-in real-event dump
# ./relayBench/run.sh --corpus f.gz --limit 50000
# ./relayBench/run.sh --relay 'myrelay=/path/bin --port {port} --db {dir}'
#
# strfry resolution order: $STRFRY_BIN → `strfry` on PATH → build from
# source into relayBench/.cache (needs the deps listed in the error text).
# Skip strfry entirely with SKIP_STRFRY=1; skip geode with SKIP_GEODE=1.
set -euo pipefail
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ROOT="$(dirname "$HERE")"
CACHE="$HERE/.cache"
ARGS=()
for arg in "$@"; do
case "$arg" in
--real)
ARGS+=("--corpus" "$ROOT/quartz/src/commonTest/resources/nostr_vitor_startup_data.json.gz")
;;
*)
ARGS+=("$arg")
;;
esac
done
# ---------- strfry ----------
resolve_strfry() {
if [[ -n "${STRFRY_BIN:-}" ]]; then
echo "$STRFRY_BIN"
return
fi
if command -v strfry > /dev/null 2>&1; then
command -v strfry
return
fi
if [[ -x "$CACHE/strfry/strfry" ]]; then
echo "$CACHE/strfry/strfry"
return
fi
echo "building strfry from source (first run only — a few minutes)…" >&2
mkdir -p "$CACHE"
if [[ ! -d "$CACHE/strfry" ]]; then
git clone --depth 1 https://github.com/hoytech/strfry.git "$CACHE/strfry" >&2
fi
(
cd "$CACHE/strfry"
git submodule update --init --depth 1 >&2
make setup-golpe >&2
make -j"$(nproc)" >&2
) || {
cat >&2 << 'EOF'
strfry build failed. On Debian/Ubuntu install its deps with:
sudo apt install build-essential libyaml-perl libtemplate-perl \
libregexp-grammars-perl libssl-dev zlib1g-dev liblmdb-dev \
libflatbuffers-dev libsecp256k1-dev libzstd-dev
or point STRFRY_BIN at an existing binary, or SKIP_STRFRY=1 to bench without it.
EOF
return 1
}
echo "$CACHE/strfry/strfry"
}
STRFRY=""
if [[ "${SKIP_STRFRY:-0}" != "1" ]]; then
STRFRY="$(resolve_strfry)" || exit 1
echo "strfry: $STRFRY"
fi
# ---------- build geode + harness ----------
echo "building geode + relaybench…"
GRADLE_TASKS=(":relayBench:installDist")
if [[ "${SKIP_GEODE:-0}" != "1" ]]; then
GRADLE_TASKS+=(":geode:installDist")
fi
(cd "$ROOT" && ./gradlew -q "${GRADLE_TASKS[@]}")
RELAY_FLAGS=()
if [[ "${SKIP_GEODE:-0}" != "1" ]]; then
RELAY_FLAGS+=("--geode-bin" "$ROOT/geode/build/install/geode/bin/geode")
fi
if [[ -n "$STRFRY" ]]; then
RELAY_FLAGS+=("--strfry-bin" "$STRFRY")
fi
# ---------- run ----------
cd "$ROOT"
exec "$ROOT/relayBench/build/install/relaybench/bin/relaybench" \
"${RELAY_FLAGS[@]}" \
"${ARGS[@]}"