commit a95a107bb851785377fb65d884a21900f708f105
parent 424a14337f392b3dedaf68c95051ab66d93ef8e7
Author: Silas Brack <silasbrack@gmail.com>
Date: Sun, 14 Jun 2026 15:18:00 +0200
add bench.sh for repeatable read/write benchmarks
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Diffstat:
| A | bench.sh | | | 85 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
1 file changed, 85 insertions(+), 0 deletions(-)
diff --git a/bench.sh b/bench.sh
@@ -0,0 +1,85 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+PORT=8005
+DB_PATH=/tmp/swa-bench/app.db
+ADDR="0.0.0.0:$PORT"
+BASE="http://localhost:$PORT"
+HEY=$(nix build nixpkgs#hey --print-out-paths --no-link 2>/dev/null)/bin/hey
+SQLITE=$(nix build nixpkgs#sqlite --print-out-paths --no-link 2>/dev/null)/bin/sqlite3
+
+# Build release
+echo "Building release..."
+nix develop -c cargo build --release 2>&1 | tail -1
+
+# Clean start
+pkill -f "simple-web-app" 2>/dev/null || true
+sleep 1
+rm -rf /tmp/swa-bench && mkdir -p /tmp/swa-bench
+
+DB_PATH=$DB_PATH ADDR=$ADDR DEBUG=false \
+ SMTP_ADDR=localhost:25 SMTP_FROM=x@x.com SITE_URL=$BASE \
+ ./target/release/simple-web-app &>/dev/null &
+sleep 2
+
+# Register, verify, login
+curl -s -o /dev/null -X POST -d "username=bench&email=bench@test.com&password=pass123&password_confirm=pass123" "$BASE/register"
+$SQLITE $DB_PATH "UPDATE user SET email_verified=1;"
+curl -s -c /tmp/swa-bench-cookies.txt -L -o /dev/null -X POST -d "email=bench@test.com&password=pass123" "$BASE/login"
+COOKIE=$(grep session /tmp/swa-bench-cookies.txt | awk '{print $6"="$7}')
+
+# Seed data
+STORIES=${STORIES:-200}
+COMMENTS_PER_STORY=${COMMENTS_PER_STORY:-5}
+COMMENT_STORIES=20
+
+echo "Seeding $STORIES stories, $((COMMENT_STORIES * COMMENTS_PER_STORY)) comments..."
+$SQLITE $DB_PATH "
+BEGIN;
+$(for i in $(seq 1 $STORIES); do
+ echo "INSERT INTO story (title, url, text, domain, published, language, created_by, score) VALUES ('Story $i about interesting things', 'https://example.com/$i', 'Body text for story $i with words for FTS.', 'example.com', $(date +%s), 'en', 1, $((RANDOM % 50)));"
+done)
+$(for s in $(seq 1 $COMMENT_STORIES); do
+ for c in $(seq 1 $COMMENTS_PER_STORY); do
+ echo "INSERT INTO comment (story_id, parent_id, path, depth, text, created_by, created_at) VALUES ($s, NULL, '$c', 0, 'Comment $c on story $s with discussion text', 1, $(date +%s));"
+ done
+done)
+COMMIT;
+INSERT INTO story_fts(story_fts) VALUES('rebuild');
+"
+
+echo ""
+echo "=== Data ==="
+$SQLITE $DB_PATH "
+SELECT 'stories: ' || COUNT(*) FROM story;
+SELECT 'comments: ' || COUNT(*) FROM comment;
+SELECT 'users: ' || COUNT(*) FROM user;
+"
+
+run() {
+ local label=$1 url=$2 c=$3 n=${4:-$((c * 50))} method=${5:-GET} extra="${6:-}"
+ local result
+ result=$($HEY -n "$n" -c "$c" -m "$method" $extra "$url" 2>&1)
+ local rps=$(echo "$result" | grep "Requests/sec" | awk '{print $2}')
+ local avg=$(echo "$result" | grep "Average" | awk '{print $2}')
+ local p99=$(echo "$result" | grep "99%" | awk '{print $3}')
+ printf "%-20s c=%-4s %10s rps avg=%ss p99=%ss\n" "$label" "$c" "$rps" "$avg" "$p99"
+}
+
+echo ""
+echo "=== READS ==="
+for c in 10 100 500; do run "homepage" "$BASE/" $c; done
+for c in 10 100 500; do run "story" "$BASE/story/1" $c; done
+for c in 10 100; do run "search" "$BASE/search" $c $((c * 20)) POST "-d search=interesting -H Content-Type:application/x-www-form-urlencoded"; done
+
+echo ""
+echo "=== WRITES ==="
+for c in 1 10 50; do run "vote" "$BASE/vote/story/1" $c $((c * 50)) POST "-H Cookie:$COOKIE"; done
+for c in 1 10; do run "submit" "$BASE/submit" $c $((c * 20)) POST "-H Cookie:$COOKIE -d title=Bench&url=https://example.com/b&text=&tags="; done
+
+echo ""
+echo "=== CEILING ==="
+run "static" "$BASE/static/datastar.js" 200 10000
+
+# Cleanup
+pkill -f "simple-web-app" 2>/dev/null || true