Benchmarking
This page shows how to measure a server’s throughput and latency with ricochet-bench, an Apache Bench-style
tool that drives every protocol the server serves.
Build it
Section titled “Build it”ricochet-bench is not in the release assets. Build it from the source tree with Go 1.25.7 or later:
git clone --branch v1.1.0 https://github.com/stephanfeb/go-ricochet.gitcd go-ricochetgo build -o ricochet-bench ./cmd/ricochet-benchricochet-bench [flags] <server-multiaddr> <server-peer-id>The multiaddr is the server’s address without the /p2p/ part; the peer ID comes separately. Both are in the
server’s startup output. See First run & your peer ID.
| Flag | Default | Meaning |
|---|---|---|
-n |
1000 |
Total requests to measure |
-c |
10 |
Concurrent workers |
-protocol |
msa |
Scenario to run (see below) |
-payload-size |
1024 |
Payload size in bytes |
-duration |
none | Run for a duration instead of a fixed count, for example 30s or 1m |
-warmup |
10 |
Warmup requests before measuring |
-batch-size |
100 |
Documents or messages per request, for sda-batch and msa-batch |
-docs |
500 |
Vault size for the sync-* scenarios |
-v |
off | Show libp2p and UDX diagnostic logs |
If -c is larger than -n and no -duration is given, -c is lowered to -n.
Scenarios
Section titled “Scenarios”-protocol |
Operation | Measures |
|---|---|---|
msa |
SendMessage |
Message submission |
maa |
RetrieveMessages |
Message retrieval; seeds a mailbox first |
sda |
PutDocument + GetDocument |
Document round trip |
sfa |
AppendFeedEntry + GetFeedEntry |
Feed append and read; creates feeds first |
sca |
PutCollectionItem + QueryCollection |
Collection write and query; creates collections first |
mma |
CreateMailbox + DeleteMailbox |
Mailbox admin round trip |
mixed |
Random mix of the six above | Combined workload |
sda-batch |
PutDocuments |
Batched document writes, -batch-size per request |
msa-batch |
SendMessages |
Batched submission, -batch-size per request |
sync-cold |
PutDocument × -docs |
First upload of a vault, every document new |
sync-warm |
PutDocument × -docs |
Re-upload after edits, every document replaced |
sync-noop |
ListDocuments + compare |
Nothing changed: list and compare ETags, write nothing |
In the sync-* scenarios one “request” is a whole vault pass. Their defaults change accordingly: unless you set
them, -n becomes 10, -warmup 1 and -c 1.
Examples
Section titled “Examples”# Message submissionricochet-bench -n 1000 -c 10 -protocol msa \ /ip4/127.0.0.1/udp/55223/udx 12D3KooW...
# Documents with 4 KB payloadsricochet-bench -n 5000 -c 20 -protocol sda -payload-size 4096 \ /ip4/127.0.0.1/udp/55223/udx 12D3KooW...
# A one-minute mixed workloadricochet-bench -duration 60s -c 50 -protocol mixed \ /ip4/127.0.0.1/udp/55223/udx 12D3KooW...
# Batched writes, 100 documents per requestricochet-bench -n 200 -c 8 -protocol sda-batch -batch-size 100 \ /ip4/127.0.0.1/udp/55223/udx 12D3KooW...Reading the output
Section titled “Reading the output”A run against a development server on the same laptop, with PostgreSQL local:
======================================== Ricochet Bench - MSA (Message Submission)========================================Server: /ip4/127.0.0.1/udp/55999/udx/p2p/12D3KooW...oY6B3C4R
[1/4] Creating 10 workers... done[2/4] Connecting to server... connected[3/4] Warming up (10 requests)... done[4/4] Benchmarking 1000 requests with 10 workers...======================================== RESULTS========================================Concurrency Level: 10Total Requests: 1000Payload Size: 1024 bytes
Completed: 1000 Failed: 0 Total time: 1.297s Requests/sec: 770.86
Latency Distribution: min: 628.0us p50: 2.0ms p75: 6.8ms p90: 25.8ms p95: 76.6ms p99: 188.7ms max: 397.4ms========================================- Requests/sec is completed requests over total time. For the batch and sync scenarios, a second line reports
the items per second (
documents/secormessages/sec) and the total items, since one request carries many. - Latency distribution is per request, from the client’s side.
- Failed requests are summarised under
Errors:with a count per distinct message (the top five), followed by the error rate.
Ten workers on one machine do not saturate a server. Raise -c until requests per second stops rising; the
latency percentiles at that point tell you what clients will see at capacity.
How it works
Section titled “How it works”Each worker creates its own libp2p host with a fresh identity and its own connection to the server, so workers do
not contend on one connection’s stream multiplexer. Scenarios that need existing data (maa, sfa, sca,
mixed) create it before the warmup. The warmup runs before measuring and is not counted.
Ctrl+C stops a run; a second Ctrl+C exits immediately.
What to watch on the server
Section titled “What to watch on the server”While a benchmark runs, the server’s own figures tell you where the limit is:
ricochet_admission_waited_totalandricochet_admission_shed_total: requests waiting for, or shed from, admission control. Shedding means the server is at its bound. See Admission & rate limits.ricochet_db_pool_conns_acquiredagainstricochet_db_pool_conns_max: whether the database pool is the bottleneck.ricochet_request_duration_secondsby protocol and operation: the server-side share of the latency the benchmark reports.
Things to know
Section titled “Things to know”- Every worker is a new peer. Per-peer limits (
admission_control.max_in_flight_per_peer, rate limits,max_mailboxes_per_owner) apply to each worker separately. - Authentication blocks it. With
features.enable_authenticationon, the workers’ fresh identities are not on the trusted list, and the server refuses them. Benchmark a server with authentication off. - It writes real data. Benchmark against a test database, or clean up afterwards. Retention removes old messages in time, but documents, feeds and collections stay.
-cis capped by the server’s connection limit. The development preset allows only 100 connections.