Performance is deliberate, not accidental.

Yella uses a single-process runtime with a local SQLite database because that is the shape the engine is built for: durable execution, low operational overhead, predictable recovery, and no network hop between your code and your state.

This page is not a SQLite-versus-Postgres argument. It is the record of a deliberate design. The benchmark suite exists to show how that design behaves under realistic automation workloads, memory-heavy tasks, bursty traffic, degraded downstreams, and restart drills.

What we publish

Every benchmark claim should be backed by three artifacts:

Headline claims

0 SQLite errors

Across the completed benchmark, burst, soak, and restart runs published here, the harness recorded zero SQLite lock or busy errors.

Concurrency scales predictably

The API mixed workload rises from 10 runs/s at fleet concurrency 20 to 96 runs/s at fleet concurrency 200 without SQLite errors.

Memory-heavy tasks need caps

The scraper runs show the real tradeoff: raising the scraper cap improves admissions, but it raises RSS quickly.

Trust needs long runs

Burst recovery, degraded downstreams, overnight soak tests, and restart drills are part of the evidence, not optional extras.

Throughput By Fleet Concurrency

Same API-style workload, different fleet concurrency settings.

Throughput by fleet concurrency fleet c20 10 runs/s 1000ms p95 · 106MB peak RSS fleet c50 31 runs/s 1000ms p95 · 126MB peak RSS fleet c200 96 runs/s 1000ms p95 · 149MB peak RSS

Browser Cap Versus Memory

Browser-style scraper work gets expensive quickly. Cap it separately from the rest of the fleet.

Browser cap versus memory cap 1 207MB peak RSS 367 dropped · 26 runs/s cap 5 358MB peak RSS 210 dropped · 30 runs/s

Queue Pressure Under Stress

Queue growth is acceptable. The important question is whether it stays bounded and recovers.

Queue pressure under stress Burst API 47 peak queue peak queue Burst scraper 28 peak queue peak queue Degraded API 118 peak queue peak queue Degraded scraper 34 peak queue peak queue

Database Footprint After The Soak

The database grew with retained history, while the WAL stayed small. That is the storage shape we want to see.

Database footprint after the soak API mixed 971MB DB 4.2MB WAL bounded scraper 615MB DB 4.2MB WAL

Published benchmark scenarios

These are the site-facing benchmark runs. Each row links directly to the raw `.txt` report and the structured `.json` summary.

API Mixed Workload

Scenario Throughput P95 Peak RSS Dropped Retries Dead SQLite Reports
API mixed, fleet c20
Conservative concurrency on a small box
10 runs/s 1000ms 106MB 0 3 0 0 txt / json
API mixed, fleet c50
Higher I/O-bound concurrency without changing the workload shape
31 runs/s 1000ms 126MB 0 32 0 0 txt / json
API mixed, fleet c200
A practical upper sweep on one local process
96 runs/s 1000ms 149MB 0 105 0 0 txt / json

Bounded Scraper Workload

Scenario Throughput P95 Peak RSS Dropped Retries Dead SQLite Reports
Scraper bounded, cap 1
Protect a tiny box with a hard browser cap
26 runs/s 500ms 207MB 367 17 0 0 txt / json
Scraper bounded, cap 5
A practical middle ground for browser work
30 runs/s 2500ms 358MB 210 17 1 0 txt / json

The c10 scraper run is intentionally left out until its final report is available. The c1 and c5 rows already show the memory-versus-admission tradeoff clearly.

Trust checks

The public suite is only half the story. These runs are how we validate burst recovery, degraded downstream behavior, long-run stability, and restart behavior against the same SQLite store.

Scenario Key result Why it matters Reports
Burst recovery, API mixed 79 runs/s avg, peak queue 47, 0 SQLite errors Shows the queue can absorb a burst and recover cleanly when load falls. txt / json
Degraded downstream, API mixed 49 runs/s avg, p95 2500ms, dead letters 1 Shows how retries, queueing, and latency behave when downstream systems slow down. txt / json
Overnight soak, API mixed 985,742 runs completed over 6 hours, 0 SQLite errors This is the long-run stability proof for the normal automation workload. txt / json
Overnight soak, bounded scraper Peak RSS 531MB, scraper drops 2,749, 0 SQLite errors Shows what a browser-heavy workload really costs and why per-process caps matter. txt / json
Restart drill, API mixed 1 deliberate restart, 24,773 runs completed, 0 SQLite errors Shows restart and resume behavior against the same SQLite store under live load. txt / json
Restart drill, scraper load 1 deliberate restart, peak RSS 615MB, 0 SQLite errors Shows that recovery still behaves under expensive browser-like work. txt / json

Why SQLite here

SQLite is a good choice for Yella because the engine is intentionally single-process and local-disk-first. WAL mode gives concurrent readers and writers without introducing a remote database hop or a separate operational stack. The point is not theoretical maximum throughput. The point is a small, solid system with clear failure modes.

On Yella hosting, Litestream continuously ships WAL changes off-box. That gives you a low-friction recovery story on top of the local runtime: local SQLite for live execution, WAL for durability, and continuous replication for restore.

Best practices

Tune global concurrency by workload latency. Slow API-bound handlers benefit from more in-flight work. Fast, write-heavy handlers should stay more conservative. The engine concurrency docs explain the engine-side control.

Use per-process caps for expensive tasks. Browser automation, large payload transforms, and other memory-heavy work should have their own limit so they cannot dominate a small machine. We use the browser connector as a scraper-style workload in the benchmark suite, and the bounded scraper results show exactly why those caps matter.

Expect bursts to trade throughput for queueing. The right question is not whether the queue ever grows. The right question is whether it drains cleanly when the burst passes.

Measure degraded downstreams separately. When Stripe, Slack, Chromium, or an LLM slows down, that becomes your bottleneck long before SQLite does.

Use the trust suite, not just the benchmark suite. A five-minute benchmark tells you about shape. An overnight soak and a restart drill tell you whether the system is boring under stress.

When should you probably not choose Yella

If your requirement is sustained thousands of events per second with extremely tight catastrophe-loss windows, you are in distributed-log territory, not embedded-runtime territory. That comes with a very different price and operational model.

Yella is for reliable automation on small infrastructure. That boundary is deliberate.