swadhinbiswas docs: update PLAN.md, DECISIONS.md ADR-075, and BENCHMARKS.md ledger · 7/24/2026
5de743b
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
#!/usr/bin/env bash
# benchmarks/run_crud_bench.sh — Real DB-backed CRUD benchmark.
#
# Compares JustAPI (Python-handler + Rust-native), FastAPI+SQLAlchemy(async),
# and Robyn on single-row INSERT/SELECT/UPDATE/DELETE against a SQLite file
# (WAL, 10-connection pool) under `oha -c 50`.
#
# Prereqs: `cargo install oha` and the benchmark venv with justapi + fastapi
# + robyn + sqlalchemy + aiosqlite + uvicorn installed.
#
# Usage: bash benchmarks/run_crud_bench.sh
set -uo pipefail
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ROOT="$(dirname "$HERE")"
VENV_PY="$ROOT/crates/justapi-py/.venv/bin/python"
HOST="127.0.0.1"
DUR="5s"
WARM="2s"
CONC=20
DATE="$(date +%Y-%m-%d)"
log() { echo -e "\033[0;32m[crud-bench]\033[0m $*" >&2; }
warn() { echo -e "\033[1;33m[warn]\033[0m $*" >&2; }
err() { echo -e "\033[0;31m[error]\033[0m $*" >&2; }
command -v oha >/dev/null || { err "oha not found (cargo install oha)"; exit 1; }
# Pick a free base port per server to avoid collisions.
JA_PORT=8131
FA_PORT=8132
RO_PORT=8133
# measure <name> <method> <url> [body]
measure() {
local name="$1" method="$2" url="$3" body="${4:-}"
log " $name ($method $url)"
# warmup
if [[ "$method" == "POST" || "$method" == "PUT" || "$method" == "DELETE" ]]; then
oha -z "$WARM" -c "$CONC" -m "$method" -H "Content-Type: application/json" \
${body:+-d "$body"} "$url" >/dev/null 2>&1 || true
else
oha -z "$WARM" -c "$CONC" "$url" >/dev/null 2>&1 || true
fi
local jsonf
jsonf=$(mktemp /tmp/oha.XXXXXX.json)
if [[ "$method" == "POST" || "$method" == "PUT" || "$method" == "DELETE" ]]; then
oha -z "$DUR" -c "$CONC" -m "$method" -H "Content-Type: application/json" \
--output-format json ${body:+-d "$body"} "$url" > "$jsonf" 2>/dev/null || true
else
oha -z "$DUR" -c "$CONC" --output-format json "$url" > "$jsonf" 2>/dev/null || true
fi
local rps p50 p99
rps=$("$VENV_PY" -c "import json,sys;
try:
d=json.load(open('$jsonf')); print(round(d['summary']['requestsPerSec'],1))
except Exception as e: print('NA')" 2>/dev/null)
p50=$("$VENV_PY" -c "import json,sys
try:
d=json.load(open('$jsonf')); print(round(d['latencyPercentiles']['p50']/1e6,2),'ms')
except Exception: print('NA')" 2>/dev/null)
p99=$("$VENV_PY" -c "import json,sys
try:
d=json.load(open('$jsonf')); print(round(d['latencyPercentiles']['p99']/1e6,2),'ms')
except Exception: print('NA')" 2>/dev/null)
rm -f "$jsonf"
echo "| $name | $rps | $p50 | $p99 |"
}
start_server() {
local name="$1" port="$2"; shift 2
log "Starting $name on :$port"
cd "$ROOT"
local logf="/tmp/crud_bench_${name//[^a-zA-Z0-9]/_}.log"
"$@" >"$logf" 2>&1 &
local pid=$!
sleep 3
local tries=20
while ! curl -s "http://$HOST:$port/items/1" >/dev/null 2>&1; do
tries=$((tries-1))
if [[ $tries -le 0 || ! -d "/proc/$pid" ]]; then
err "$name failed to start (see $logf)"; tail -5 "$logf" >&2
kill "$pid" 2>/dev/null || true; return 1
fi
sleep 1
done
echo "$pid"
}
stop_server() {
local pid="$1" name="$2"
[[ -d "/proc/$pid" ]] && { kill "$pid" 2>/dev/null; wait "$pid" 2>/dev/null; sleep 1; }
log "Stopped $name"
}
BODY='{"name":"widget","qty":7}'
echo ""
echo "## Real DB-backed CRUD benchmark (recorded $DATE)"
echo ""
echo "- **Workload:** single-row INSERT / SELECT / UPDATE / DELETE, SQLite file (WAL, busy_timeout=5000, 10-conn pool)"
echo "- **Note:** single-file SQLite write-serializes (one writer at a time), so write"
echo " RPS is SQLite-bound (~hundreds) and similar across frameworks; SELECT is the"
echo " framework-differentiated metric."
echo "- **Tool:** \`oha -c $CONC -z $DUR\`"
echo "- **CPU:** $(grep 'model name' /proc/cpuinfo | head -1 | cut -d: -f2 | xargs) ($(nproc) threads)"
echo "- **Kernel:** $(uname -sr)"
echo ""
# ---- JustAPI (Python-handler) ----
PID=$(start_server "JustAPI(py)" "$JA_PORT" "$VENV_PY" "$HERE/crud_justapi.py" python "$JA_PORT") || true
if [[ -n "${PID:-}" ]]; then
echo "### JustAPI — Python-handler CRUD"
echo ""
echo "| Operation | RPS | p50 | p99 |"
echo "|-----------|-----|-----|-----|"
measure "INSERT" POST "http://$HOST:$JA_PORT/items" "$BODY"
measure "SELECT" GET "http://$HOST:$JA_PORT/items/1"
measure "UPDATE" PUT "http://$HOST:$JA_PORT/items/1" "$BODY"
measure "DELETE" DELETE "http://$HOST:$JA_PORT/items/1"
echo ""
stop_server "$PID" "JustAPI(py)"
fi
# ---- JustAPI (Rust-native) ----
PID=$(start_server "JustAPI(native)" "$JA_PORT" "$VENV_PY" "$HERE/crud_justapi.py" native "$JA_PORT") || true
if [[ -n "${PID:-}" ]]; then
echo "### JustAPI — Rust-native CRUD (fast path)"
echo ""
echo "| Operation | RPS | p50 | p99 |"
echo "|-----------|-----|-----|-----|"
measure "INSERT" POST "http://$HOST:$JA_PORT/items" "$BODY"
measure "SELECT" GET "http://$HOST:$JA_PORT/items/1"
measure "UPDATE" PUT "http://$HOST:$JA_PORT/items/1" "$BODY"
measure "DELETE" DELETE "http://$HOST:$JA_PORT/items/1"
echo ""
stop_server "$PID" "JustAPI(native)"
fi
# ---- FastAPI + SQLAlchemy ----
PID=$(start_server "FastAPI" "$FA_PORT" "$VENV_PY" "$HERE/crud_fastapi.py" "$FA_PORT") || true
if [[ -n "${PID:-}" ]]; then
echo "### FastAPI + SQLAlchemy (async)"
echo ""
echo "| Operation | RPS | p50 | p99 |"
echo "|-----------|-----|-----|-----|"
measure "INSERT" POST "http://$HOST:$FA_PORT/items" "$BODY"
measure "SELECT" GET "http://$HOST:$FA_PORT/items/1"
measure "UPDATE" PUT "http://$HOST:$FA_PORT/items/1" "$BODY"
measure "DELETE" DELETE "http://$HOST:$FA_PORT/items/1"
echo ""
stop_server "$PID" "FastAPI"
fi
# ---- Robyn ----
PID=$(start_server "Robyn" "$RO_PORT" "$VENV_PY" "$HERE/crud_robyn.py" "$RO_PORT") || true
if [[ -n "${PID:-}" ]]; then
echo "### Robyn (sync handler, sqlite3)"
echo ""
echo "| Operation | RPS | p50 | p99 |"
echo "|-----------|-----|-----|-----|"
measure "INSERT" POST "http://$HOST:$RO_PORT/items" "$BODY"
measure "SELECT" GET "http://$HOST:$RO_PORT/items/1"
measure "UPDATE" PUT "http://$HOST:$RO_PORT/items/1" "$BODY"
measure "DELETE" DELETE "http://$HOST:$RO_PORT/items/1"
echo ""
stop_server "$PID" "Robyn"
fi
# Cleanup bench db
for ext in "" "-wal" "-shm"; do rm -f "$HERE/crud_bench.sqlite$ext"; done
log "Done. Paste the tables above into BENCHMARKS.md."