Repository navigation
Releases
v1.4.0
After the 2,000,000 run, the question I kept getting was: what about a real
database? So I load tested a chat app that keeps every message in Postgres,
with 100 million messages in it, and it held 40,000 users on one 16-core
server, with Postgres on a machine of its own. That test is why the probe now
watches your database, and why a --find-limit verdict tells you which part
gave out. Runs also show up where you already work now, in your app's
LiveDashboard and in Livebook.
--probewatches your database too. It sends how long queries waited for
a connection from the Ecto pool every second, and for a Postgres repo it
reads Postgres's own commits, writes, reads, connections and waits, over a
connection of its own outside your pool. The dashboard and the HTML report
chart them- A
--find-limitverdict says why the step failed when the probe can tell:
queries waiting on the pool, what Postgres was waiting on, such as writing
its log to disk, and whether the app's own schedulers were the limit - A Stampede page for Phoenix LiveDashboard,
Stampede.LiveDashboard. Add
it to your app's dashboard, and a run that probes the app shows in a
Stampede tab while it runs, next to the dashboard's own pages. Finished
runs stay there, with the same results as the HTML report - Load tests in Livebook. With Kino in the notebook, a Load test smart
cell writes and runs a scenario for a page, a LiveView, a channel or an AI
chat API, and can probe your app's node.Stampede.Kino.run/2shows any
run in the notebook while it goes, and anyStampede.Resultshows as the
report - The bench has a new result: a chat app that keeps every message in
Postgres, seeded with 100 million messages, held 40,000 users with no
errors on one 16-core server, with Postgres on an 8-core machine of its
own
The full test, with the chat app, the scripts and every log, is in
bench/million.
v1.3.2
The README has the newest result: one 16-core Phoenix server held 2,000,000 users with no errors.
v1.3.1
The README's images and links now work on hex.pm, and it no longer says that signing in isn't recorded.
v1.3.0
This one is about real apps. You can record yourself using your app and get a
scenario, signing in included, and see inside the app during a run without
changing it. Stampede can also load test AI chat APIs now, and it found new limits
for me: stock Phoenix held 600,000 Channels users on one 4-core server, and a real
open source Phoenix product held 50,000 users for 10 minutes on the same size of
machine. Everything needed to run those again is in bench/million.
mix stampede.recordstarts your app and records what you do in the
browser, then writes it as a scenario, with your clicks, forms, uploads,
channels, navigation and pauses, and signing in through a form. Passwords
and other sensitive fields become"CHANGE_ME", in forms, in JSON and in
URLs. It's new, so it may still changeStampede.HTTPkeeps cookies withcookies: true, one set per VU and per
host, andStampede.HTTP.clear_cookies/0forgets them.:cookieson
Stampede.LiveView.connect/2andStampede.SSEshares them, so a scenario
can sign in over HTTP and then open a LiveView as that userStampede.HTTP's:formoption sends a form as a browser would, and
Stampede.HTTP.csrf_token/1reads the CSRF token out of a page- A guide to watching a run in Prometheus and Grafana
Stampede.Channels.connect/2takes a:keepfunction, for VUs in busy
channels that hear far more than they read--probe app@host, orprobe:from code, watches the app from the inside
during a run, with no changes to it: scheduler use, memory, processes, run
queue and the longest message queues, charted in the dashboard and the
HTML report, and Phoenix, LiveView and Ecto timings asserver.*metrics.
A--find-limitverdict says when the server's schedulers ran out, or
when it went downStampede.LLMload tests AI chat APIs in OpenAI's format, such as Ollama,
vLLM or llama.cpp. It records time to first token, the gaps between
tokens, time per token and tokens per second, measured the same way as
vllm bench serve. It's new, so it may still changeStampede.LLM.prompts_from_jsonl/2reads conversations from benchmark
datasets, andStampede.LLM.synthetic_prompt/1makes prompts of a given
lengthStampede.SSEreads Server-Sent Events from any server, with when each
event arrivedmix stampede.gen.scenario --llmgenerates a scenario for a model server- The dashboard and the HTML report show tokens per second and time to
first token for runs that chat with a model server
v1.2.0
This one answers the question I get asked most: how many users can my app
handle? It also comes out of a real test on AWS, where stock Phoenix on one
4-core server held 400,000 Channels users with no errors. That run found
the problems with Stampede itself fixed below, and everything needed to run
it again is in bench/million.
--find-limit, orfind_limit:from code, answers "how many users can
it handle?": the load goes up a step at a time until a threshold fails,
and the result says the most users, or the highest rate, that passed.
It's new, so its options and report may still change--badge, andStampede.Reporter.to_badge/1, write a README badge with
the limit foundmix stampede.gen.scenario --rategenerates a scenario that starts
iterations at a ratemix stampede.compare --format markdown, to post a comparison on a pull
request- Finished runs on the dashboard can be downloaded as the HTML report or
the JSON - Channels and WebSocket connections hibernate after 5 seconds without
traffic. A VU holding a Channels connection went from about 37 KB of
memory to 23 KB, which matters once you're holding hundreds of thousands - WebSocket connections use a 64 KB socket buffer instead of the few
megabytes Mint sets. With 60,000 connections, that cut the load test's
CPU by about a fifth - Runs measure how busy the load test itself is, every second. When it's
90% busy or more, its own delays end up in its timings, so the summary
and the HTML report say so, and a--find-limitverdict says the limit
may be the load test's rather than the server's - A run ends on time and prints its summary even when its VUs keep the
machine 100% busy. Its own processes run at high priority, and progress
is printed without holding the run up - A VU whose iterations keep failing waits longer between tries: 100 ms,
then 150, 200, 250, 500 ms, 1 s, 2 s and at most 5 s, much like Phoenix's
JavaScript client before it reconnects. It was 100 ms every time, which
piled more load onto a server, or a load test, that was already in
trouble - Connections no longer go through Erlang's host lookup for every
connect. IP addresses are used as they are, and host names are looked
up once and kept for 5 minutes. Under heavy load, lookups queued behind
each other and timed out asnxdomainerrors that looked like the
server failing - A run warns before it starts when a node may not allow enough processes
or open files for its VUs, instead of failing halfway with errors that
don't say why. It checksulimit -ntoo, which the BEAM doesn't report
v1.1.0
This release is mostly about seeing what a load test is doing while it runs,
and about covering more of a Phoenix app.
- A live dashboard:
mix stampede.run --dashboard, orStampede.Dashboard
from code --parallelandStampede.run_parallel/2run several scenarios at once,
each with its own options- LiveView uploads with
Stampede.LiveView.upload/4, recorded as
"live.upload" mix stampede.gen.scenariogenerates HTTP, Channels, LiveView and
WebSocket scenariosmix igniter.install stampedeadds Stampede and a first scenario to a
projectStampede.ExUnitruns load tests from a test suite- Binary payloads in
Stampede.Channelswith the"2.0.0"serializer, as
{:binary, data} - Custom serializers through
Stampede.Channels.Serializerand the
:serializeroption, for servers that extend the protocol - Elixir 1.18 is supported
- New guides: pages behind a login, Phoenix Presence, Absinthe
subscriptions, load tests in CI, spreading the load over machines, and a
case study with real numbers
Fixes
- A payload that cannot be encoded raises in the caller of
push/5or
cast/4, instead of crashing the socket.
v1.0.0
First release of Stampede, load testing for Phoenix with built-in Channels and LiveView support.
- LiveView: loads pages like a browser and sends clicks, form changes, submits, patches and navigation
- Phoenix Channels, HTTP and plain WebSockets
- VUs with stages, or a fixed arrival rate, with graceful stop
- Checks and thresholds that fail CI, JSON and HTML reports, and mix stampede.compare for regressions
- One test spread over several nodes
See the README to get started.