Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

14 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

jmap-perftest

Performance-Test-Suite für JMAP-Server (RFC 8620/8621). Misst gleichzeitige Zugriffe und Requests/s für Lese- und Schreiboperationen und vergleicht mehrere Server bzw. Storage-Backends (Filesystem/RocksDB, PostgreSQL, S3).

Schnellstart

# Suite installieren
uv venv .venv && uv pip install -e . -p .venv/bin/python
# oder: pip install -e .

# Lokale Server starten (Docker)
cd docker && docker compose up -d && ./provision.sh && cd ..

# Konfigurierte Server anzeigen und prüfen
jmap-bench servers --check

# Testdaten einspielen und Benchmark fahren
jmap-bench run --server stalwart_rocksdb --seed --concurrency 1,10,50 --duration 15
jmap-bench run --server cyrus            --seed --concurrency 1,10,50 --duration 15

# Vergleichsreport (Markdown)
jmap-bench compare results/*.json --out results/comparison.md

Externe Server per .env

Zugangsdaten kommen aus .env (gitignored, siehe .env.example):

# Einfachste Form (Servername: "remote")
JMAP_SERVER=mail.example.com
JMAP_ACCOUNT=user@example.com
JMAP_PASSWORD=secret

# Oder mehrere Server:
JMAP_SERVERS=fastmail,prod
JMAP_FASTMAIL_URL=https://api.fastmail.com/.well-known/jmap
JMAP_FASTMAIL_TOKEN=fmu1-...
JMAP_PROD_URL=https://mail.firma.de
JMAP_PROD_USER=bench@firma.de
JMAP_PROD_PASS=secret
jmap-bench run --server remote --seed --concurrency 1,10,50

Hinweis: Die Suite legt auf dem Zielkonto zwei Ordner an (PerfTest mit Seed-Mails, PerfWrite für Schreibtests) und räumt erzeugte Mails nach jedem Szenario wieder ab. Trotzdem: nur gegen Test-Accounts laufen lassen.

Szenarien

Szenario Art Beschreibung
mailbox_get read Mailbox/get, alle Ordner
email_query read Email/query, neueste 20 einer Mailbox
email_query_get read Query + Email/get in einem Request (Inbox-Refresh)
email_get_body read Voller Body einer zufälligen Mail
email_set_create write Email/set create (~--body-size Bytes)
email_import write Blob-Upload + Email/import (≈ Zustellung)
email_set_flags write Keyword-Toggle (kleiner Metadaten-Write)
mixed_80_20 mixed 80 % Lesen / 20 % Schreiben

Jedes Szenario läuft pro Concurrency-Stufe (--concurrency 1,10,50,100) --warmup Sekunden warm und misst dann --duration Sekunden. Ausgegeben werden op/s, Latenz p50/p95/p99/max und Fehlerzahl; Ergebnisse landen als JSON unter results/.

Lokale Server & Backends

docker/compose.yaml startet:

Name Port Server Backend
stalwart_rocksdb 8080 Stalwart RocksDB (Filesystem, embedded)
stalwart_postgres 8081 Stalwart PostgreSQL (alle Stores)
stalwart_s3 8082 Stalwart RocksDB-Metadaten + S3-Blobs (MinIO)
cyrus 8090 Cyrus IMAP 3.8 twoskip/Filesystem

Diese vier Namen sind in der Suite vorkonfiguriert (jmap-bench servers) und brauchen keine .env-Einträge. Account: perf@example.com / jmap-perf-2026-Zk8xQvNw (Stalwart v0.16 lehnt schwache Passwörter ab; Cyrus: perf, Passwort beliebig — Auth ist im Testmodus).

Die Docker-Variante läuft auf Stalwart v0.16 (Konfiguration liegt im Datastore, docker/provision.sh legt Domain/Account an und hebt die Throttles per JMAP-Registry-API an). Der native Build unten nutzt weiterhin 0.15.6 mit TOML-Konfiguration.

Cyrus kennt nur sein eigenes Filesystem-Backend; die Backend-Matrix (DB/S3) gibt es nur bei Stalwart.

Ohne Docker (nativ)

Falls Docker nicht verfügbar ist (z. B. eingeschränkte CI-Umgebung):

# Cyrus (Ubuntu/Debian)
apt install cyrus-imapd cyrus-common cyrus-caldav cyrus-admin sasl2-bin
sudo servers/cyrus/start-cyrus.sh

# Stalwart aus dem Quellcode (crates.io) mit allen drei Backends
cargo install stalwart --locked --features "postgres s3" --root /opt/stalwart-bin
# Achtung: das crates.io-Paket 0.15.6 hat einen Packaging-Bug (4× E0433 im
# DAV-Modul). Workaround: in ~/.cargo/registry/src/*/stalwart-0.15.6/crates/dav/src/calendar/
# in copy_move.rs und delete.rs `crate::DavErrorCondition` → `crate::dav::DavErrorCondition`
# und `dav_proto::schema::…` → `crate::dav_proto::schema::…` ersetzen, dann erneut bauen.
# (Mit Docker-Image nicht nötig.)
sudo servers/stalwart/start-stalwart.sh rocksdb
sudo servers/stalwart/start-stalwart.sh postgres   # braucht laufendes PostgreSQL (DB/User: stalwart)
sudo servers/stalwart/start-stalwart.sh s3         # braucht MinIO auf :9000 mit Bucket "stalwart"

Ergebnisse & Vergleich

jmap-bench compare results/stalwart_rocksdb.json results/cyrus.json --out results/comparison.md

erzeugt Markdown-Tabellen pro Szenario (op/s und p95 je Server und Concurrency-Stufe) plus eine Peak-Throughput-Übersicht. Beispielreport: results/comparison.md.

Erste Ergebnisse (lokal, 4 Kerne, Client+Server auf derselben Maschine)

Peak-Durchsatz (op/s, bestes Ergebnis über Concurrency 1/10/50, 500 Mails à 4 KB, vollständige Tabellen in results/comparison.md):

Szenario Stalwart RocksDB Stalwart PostgreSQL Stalwart RocksDB+S3 Cyrus 3.8
email_query 871 635 1236 1156
email_query_get 717 420 811 270
email_get_body 1432 663 2215 1042
email_set_create 409 203 421 68
email_import 191 137 193 40
email_set_flags 1638 536 1669 147
mixed_80_20 301 396 393 152

Kernbefunde:

  • Schreiben: Stalwart (RocksDB) schafft ~6× so viele Email-Creates und ~10× so viele Flag-Updates wie Cyrus. Cyrus serialisiert Writes pro Mailbox (Lock) — mehr Concurrency erhöht dort nur die Latenz (p95 bei c=50: 869 ms create), nicht den Durchsatz.
  • Lesen: Cyrus ist bei Einzel-Query/Body-Reads konkurrenzfähig (schnelle twoskip-Indizes), fällt aber beim kombinierten query+get (Inbox-Refresh) deutlich zurück (270 vs. 717–811 op/s).
  • Backends (Stalwart): PostgreSQL kostet gegenüber RocksDB je nach Szenario 25–70 % Durchsatz (jeder Store-Zugriff geht übers Netz zur DB). S3-Blobs kosten beim Import Latenz (p95 44 ms vs. 9 ms bei c=1), skalieren aber gut, weil nur Blobs ausgelagert sind und Metadaten in RocksDB bleiben.
  • Alle Läufe fehlerfrei; Stalwart-Defaults (4 parallele Requests, 1000 req/min Rate-Limit, Upload-Quota) wurden für die Messungen angehoben — sonst misst man nur die Drossel.

Interpretationshinweise

  • op/s zählt logische Operationen; email_import besteht aus zwei HTTP-Requests (Upload + Import).
  • Läufe auf derselben Maschine wie der Server messen auch den Client-Overhead; für belastbare Zahlen Suite und Server trennen.
  • Cyrus serialisiert Schreibzugriffe auf eine Mailbox (Mailbox-Lock) — mehr Concurrency erhöht dort nur die Latenz, nicht den Durchsatz.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages