Benchmarks solid_queue under realistic load, comparing the official gem against a local fork. It writes traceable JSON results and markdown/HTML comparison reports.
- mise for the pinned Ruby version
- Docker for MySQL 8.0 and Postgres 16
libpqdev headers on the host, to compile thepggem's native extension (libpq-devon Debian/Ubuntu,postgresqlvia Homebrew on macOS)
Workers run on the host. Only the database (MySQL or Postgres) is containerized.
mise install
mise run setupIf mise reports that mise.toml is not trusted, run mise trust once in the checkout, then retry.
setup bundles the default upstream source and pulls both the MySQL and Postgres images.
# latest official gem, against MySQL (default)
bin/bench run baseline --source upstream
# same run against Postgres, for a like-for-like comparison
bin/bench run baseline --source upstream --database postgres
# pinned RubyGems release
bin/bench run baseline --source upstream@1.2.4
# local fork; must be clean unless --allow-dirty is passed
bin/bench run sprawl --source path:~/Projects/solid_queueEach run starts a fresh database volume (MySQL or Postgres, per --database; default mysql),
loads schema from the selected gem source, starts Solid Queue workers, runs the scenario, waits
for drain, and writes results/<run-id>/result.json plus logs. Results include the resolved gem
version, the database engine used, the process launcher, and, for path: sources, the git SHA.
| Scenario | Params (--set key=val) |
Measures |
|---|---|---|
baseline |
jobs=20000 rate=500 work_ms=50 duration=60 |
Steady-state throughput and latency. jobs=0 measures idle polling for duration seconds. |
baseline_limited |
jobs=1000 rate=500 work_ms=50 |
Same job work as baseline, with a class-level limits_concurrency limit of 1 for blocked-execution and throttling cost. |
sprawl |
seeds=100 fanout=50 depth=2 work_ms=10 |
Fan-out burst where each job enqueues fanout children down to depth. Defaults total 255,100 jobs; reduce params for quick runs. |
Examples:
bin/bench run baseline --source upstream --profile smoke --set jobs=100 --set rate=100 --set work_ms=0 --timeout 180
bin/bench run baseline --source upstream --set jobs=1000 --set rate=500 --set work_ms=50
bin/bench run baseline_limited --source upstream --set jobs=1000 --set rate=500 --set work_ms=50
bin/bench run sprawl --source path:~/Projects/solid_queue --set seeds=5 --set fanout=10 --set depth=1Profiles live in profiles/*.yml and bundle database resource limits with worker topology so
comparisons stay comparable, independent of which engine a run targets. The default profile
pins the database container to 1 CPU / 1 GB, with 10 worker processes x 2 threads and 1
dispatcher.
bin/bench run baseline --source upstream --profile default
bin/bench run baseline --source upstream --profile smoke
bin/bench run baseline --source upstream --workers 50 --db-cpus 2 --db-memory 2g
bin/bench run baseline --source upstream --database postgres --profile defaultbin/bench compare refuses profile mismatches unless --force is passed. Comparing a MySQL run
against a Postgres run under the same profile is not treated as a mismatch — the report simply
shows which engine each side used.
By default, the harness uses Solid Queue's fork supervisor. On macOS with Postgres, the harness
uses direct launching instead: each worker and dispatcher is started as a fresh Ruby process to
avoid the pg/libpq crashes seen when forked children connect after Rails has booted. The chosen
launcher is recorded in result.json under profile.process_launcher, so mixed launcher
comparisons are guarded like other topology changes. Override with
BENCH_SOLID_QUEUE_LAUNCHER=supervisor or BENCH_SOLID_QUEUE_LAUNCHER=direct when needed.
bin/bench list
bin/bench compare results/<A>/result.json results/<B>/result.jsonReports are written to reports/<A>__vs__<B>/report.md and report.html. They include which
database engine each run used, metric deltas, DB CPU and ready-depth overlay charts, and top SQL
statements by total database time.
Use --force only when you intentionally want to compare different profiles:
bin/bench compare results/<A>/result.json results/<B>/result.json --forcebin/bench run baseline --source upstream --repeat 3Repeats keep every raw result and print median throughput across completed runs.
- Throughput, including a per-second series
- Latency percentiles for enqueue-to-start and enqueue-to-finish
- Database container CPU samples from
docker stats - Queue depth samples for ready, scheduled, claimed, blocked, failed, and completed jobs
- Top statement digests from MySQL
performance_schemaor Postgrespg_stat_statements
- Add defaults, validation, and expected-total logic in
lib/bench/scenarios.rb. - Add the enqueue branch in
harness/script/drive.rb. - Add any job class needed under
harness/app/jobs/.
Scenario params become CLI-settable through repeated --set key=val flags.
mise run test
mise run smokemise run test runs orchestrator unit tests. mise run smoke runs a 100-job upstream benchmark
against the smoke profile.