simple-web-app

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

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:
Abench.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