templates/keynote-2/DEVELOP.md
This app runs repeatable load tests against multiple connectors (Bun+Postgres, CockroachDB, SQLite, Supabase, Convex, SpacetimeDB, etc.).
Each run:
src/tests/<test-name>/./runs/ with TPS and latency statsRun a quick performance comparison:
pnpm run demo
The script will:
Options:
--seconds N - Benchmark duration (default: 300)--concurrency N - Concurrent connections (default: 64)--alpha N - Contention level (default: 1.5)--systems a,b,c - Systems to compare (default: convex,spacetimedb)--stdb-compression none|gzip - SpacetimeDB client compression mode (default: none)--skip-prep - Skip database seeding--no-animation - Disable animated outputdemo always runs the built-in test-1 scenario. Use bench if you need to choose a test explicitly.
demo uses --systems; bench uses --connectors.
From a fresh clone:
pnpm install
.env)Copy .env.example to .env and adjust.
Seeding / verification:
SEED_ACCOUNTS – number of accounts to seed (if unset, code defaults to 100_000)SEED_INITIAL_BALANCE – starting balance per accountVERIFY – enable extra verification passes when non-zeroENABLE_RPC_SERVERS – flag used by scripts that start the RPC benchmark serversRuntime toggles:
USE_DOCKER – 1 = run Docker Compose for Postgres / CockroachDB; 0 = skipSKIP_PG – 1 = don't init Postgres in prepSKIP_CRDB – 1 = don't init CockroachDB in prepSKIP_SQLITE – 1 = don't init SQLite in prepSKIP_SUPABASE – 1 = don't init Supabase in prepSKIP_CONVEX – 1 = don't init Convex in prepThroughput is counted from successful operations that the benchmark client observes completing inside the configured test window for every connector, including SpacetimeDB.
PostgreSQL / CockroachDB:
PG_URL – Postgres connection stringCRDB_URL – CockroachDB connection stringSQLite:
SQLITE_FILE – path to the SQLite fileSQLITE_MODE – tuning preset for the SQLite connectorSpacetimeDB:
STDB_URL – WebSocket URL for SpacetimeDBSTDB_MODULE – module name to load (e.g. test-1)STDB_MODULE_PATH – filesystem path to the module source (for local dev)STDB_COMPRESSION – SpacetimeDB benchmark client compression (none or gzip)STDB_CONFIRMED_READS – 1 = force confirmed reads on, 0 = force them offSupabase:
SUPABASE_URL – Supabase project URLSUPABASE_ANON_KEY – Supabase anon/public keySUPABASE_DB_URL – Postgres connection string for the Supabase databaseConvex:
CONVEX_URL – Convex deployment URLCONVEX_SITE_URL – Convex site URLCLEAR_CONVEX_ON_PREP – Convex prep flag (clears data when enabled)CONVEX_USE_SHARDED_COUNTER – flag for using the sharded-counter implementationPlanetScale:
PLANETSCALE_PG_URL – Postgres connection stringPLANETSCALE_RPC_URL – PlanetScale RPC server URL (default: http://127.0.0.1:4104)SKIP_PLANETSCALE_PG – 1 = skip PlanetScale in prepBun / RPC helpers:
BUN_URL – Bun HTTP benchmark server URLBUN_PG_URL – Postgres connection string for the Bun benchmark serviceRPC benchmark servers:
PG_RPC_URL – HTTP URL for the Postgres RPC serverCRDB_RPC_URL – HTTP URL for the CockroachDB RPC serverSQLITE_RPC_URL – HTTP URL for the SQLite RPC serverCreate a Postgres database on PlanetScale and set PLANETSCALE_PG_URL (and PLANETSCALE_RPC_URL if the RPC server runs elsewhere) in .env. Reported results used PS-2560 (32 vCPUs, 256 GB RAM).
SpacetimeDB module bindings:
cd spacetimedb
cargo run -p spacetimedb-cli -- generate --lang typescript --out-dir ../module_bindings --module-path . -y
cd ..
(Or use spacetime generate ... if the CLI is installed.)
Convex generated files:
cd convex-app
npx convex dev
# Wait for it to generate files, then Ctrl+C
cd ..
cargo run -p spacetimedb-cli -- start or spacetime start)npx convex dev)supabase init) inside project root.pnpm run prep to seed the databases.pnpm run bench to run the test against all connectors.pnpm run bench [test-name] [--seconds N] [--concurrency N] [--alpha A] [--connectors list] [--stdb-compression none|gzip]
Examples:
# Default test (test-1), default args
pnpm run bench
# Explicit test name
pnpm run bench test-1
# Short run, 100 concurrent workers
pnpm run bench test-1 --seconds 10 --concurrency 100
# Heavier skew on hot accounts
pnpm run bench test-1 --alpha 2.0
# Enable gzip for the SpacetimeDB benchmark client
pnpm run bench test-1 --connectors spacetimedb --stdb-compression gzip
# Only run selected connectors
pnpm run bench test-1 --connectors spacetimedb,sqlite_rpc
# Sweep alpha values for a connector set
pnpm run bench test-1 --alpha 0,1.5 --connectors postgres_rpc,bun --seconds 300
# Sweep contention (alpha) for a single connector: start,end,step,concurrency
pnpm run bench test-1 --connectors cockroach_rpc --contention-tests 0,1.5,0.5,64
# Sweep concurrency for a single connector: start,end,factor,alpha
pnpm run bench test-1 --connectors cockroach_rpc --concurrency-tests 16,512,2,1.5
test-name (positional)
src/tests/test-1--seconds N
300--concurrency N
64--alpha A
1.5--connectors list
Optional, comma-separated list of connector system names
Example:
--connectors spacetimedb,sqlite_rpc,postgres_rpc
If omitted, all connectors for that test are run
The valid names come from tc.system in the test modules and the keys in CONNECTORS
Valid names: convex, spacetimedb, bun, postgres_rpc, cockroach_rpc, sqlite_rpc, supabase_rpc, planetscale_pg_rpc
--systems list
--connectors in bench mode--runs N
(connector, alpha) combination N times1--prep-between-alphas
pnpm run prep before each (connector, alpha) combination--contention-tests start,end,step,concurrency
--concurrency-tests start,end,factor,alpha
--bench-pipelined / --no-bench-pipelined
--max-inflight-per-worker N
--bench-pipelined is enabled--log-errors
--verify-transactions
You can also run the benchmark via Docker instead of Node directly:
docker compose run --rm bench -- --seconds 5 --concurrency 64 --alpha 1 --connectors convex
If using Docker, make sure to set USE_DOCKER=1 in .env, verify docker-compose env variables, verify you've run supabase init, and run pnpm run prep before running bench.
Every run writes a JSON file into ./runs/:
./runs/<test-name>-<connector>-a<alpha>-<timestamp>.json
test-1-postgres_rpc-a1.5-2025-11-17T16-45-12-345Z.jsonFor rollup tables, compute steady-state stats after a 30-second warmup window (tSec >= 30). The scripts/bench-stats.py default matches this (--warmup-sec 30).
Point your visualizations / CSV exports at ./runs/ and you're good.