Skip to main content
Version: Next 🚧

Benchmarks

Orisun includes a small external gRPC benchmark runner in cmd/orisun-bench. It measures the same path used by an application: client serialization, gRPC, authentication, server processing, durable storage, and the response.

The runner must be a compiled binary, and it refuses to measure a server that reports development build metadata. This prevents accidentally publishing numbers from go run.

Results at a glance​

Choose a workload to compare backend throughput and p99 request latency across the concurrency sweep. Hover over or focus a point for its exact value. The complete result tables and test environments follow.

PostgreSQLSQLiteFoundationDB

Throughput

events/s
Comparison of PostgreSQL, SQLite, and FoundationDB at 1, 4, 16, 64, and 256 clients.05k10k15k20k141664256concurrent clientsPostgreSQL · 1 clients · 507.06 events/sPostgreSQL · 4 clients · 1,078.29 events/sPostgreSQL · 16 clients · 3,600.42 events/sPostgreSQL · 64 clients · 9,495.73 events/sPostgreSQL · 256 clients · 16,987.91 events/sSQLite · 1 clients · 2,479.52 events/sSQLite · 4 clients · 6,296.32 events/sSQLite · 16 clients · 12,930.32 events/sSQLite · 64 clients · 17,165.54 events/sSQLite · 256 clients · 18,542.24 events/sFoundationDB · 1 clients · 186.23 events/sFoundationDB · 4 clients · 697.48 events/sFoundationDB · 16 clients · 2,023.83 events/sFoundationDB · 64 clients · 3,920.14 events/sFoundationDB · 256 clients · 5,578.22 events/s

Hover or focus a point for its exact value.

p99 latency

ms
Comparison of PostgreSQL, SQLite, and FoundationDB at 1, 4, 16, 64, and 256 clients.0125250375500141664256concurrent clientsPostgreSQL · 1 clients · 5.4 msPostgreSQL · 4 clients · 9.48 msPostgreSQL · 16 clients · 11.21 msPostgreSQL · 64 clients · 15.52 msPostgreSQL · 256 clients · 34.34 msSQLite · 1 clients · 1.17 msSQLite · 4 clients · 2.25 msSQLite · 16 clients · 3.61 msSQLite · 64 clients · 10.92 msSQLite · 256 clients · 40.42 msFoundationDB · 1 clients · 16.8 msFoundationDB · 4 clients · 16.1 msFoundationDB · 16 clients · 22.24 msFoundationDB · 64 clients · 53.93 msFoundationDB · 256 clients · 219.64 ms

Hover or focus a point for its exact value.

PostgreSQL results​

The following results were recorded on July 30, 2026. All 30 measured points completed with zero foreground and background request errors.

Throughput​

Values are events per second. Conditional reads return ten events per request; projection reads return batches of up to 1,000 events.

Workload1 client4 clients16 clients64 clients256 clients
CCC append, 1 event5071,0783,6009,49616,988
CCC append, 100 events17,99726,54635,90625,35328,479
Unconditional append, 1 event4941,0633,68610,74719,889
Conditional read4,55314,70834,93148,90252,510
Projection read133,333261,598371,556356,195342,457
Append with four readers2535702,0726,15112,897

Single-event CCC append throughput remained close to unconditional append throughout the concurrency sweep. The 100-event CCC workload peaked at 16 clients; adding more concurrency increased latency without improving throughput. Projection reads also peaked at 16 clients.

Tail latency​

Values are foreground request p99 latency in milliseconds.

Workload1 client4 clients16 clients64 clients256 clients
CCC append, 1 event5.409.4811.2115.5234.34
CCC append, 100 events13.6342.4289.55498.841,323.52
Unconditional append, 1 event10.1312.6714.1115.7934.19
Conditional read7.357.8814.8444.52208.30
Projection read21.8945.03120.11379.092,946.76
Append with four readers12.9620.8921.7628.2052.55

During the mixed workload, the four background readers sustained 267,866, 276,132, 278,120, 256,387, and 205,975 events per second as foreground writer concurrency increased from 1 to 256.

Test environment​

  • Orisun v0.9.3-9-g53e5578, built as PostgreSQL server and benchmark binaries with Go 1.26.5
  • PostgreSQL 18.4 arm64 in Docker Desktop
  • synchronous_commit=on, fsync=on, and full_page_writes=on
  • Apple MacBook Pro arm64, 10 logical CPUs, 32 GB RAM, host SSD
  • benchmark runner and Orisun server on the host; PostgreSQL in local Docker
  • a fresh benchmark boundary in an existing PostgreSQL database
  • 1,000,000 seed events, 100,000 indexed contexts, and approximately 256 bytes per event
  • 5-second warm-up and 30-second measurement per point

Download the raw PostgreSQL CSV. This is a single-machine snapshot rather than a cross-product comparison.

SQLite results​

The SQLite results below use the same July 30, 2026 workload, client concurrency, dataset, payload, warm-up, and measurement duration. All 30 measured points completed with zero foreground and background request errors.

Throughput​

Values are events per second.

Workload1 client4 clients16 clients64 clients256 clients
CCC append, 1 event2,4806,29612,93017,16618,542
CCC append, 100 events38,99757,39060,76255,13657,533
Unconditional append, 1 event2,0656,68813,97922,83827,280
Conditional read10,11026,10577,26391,56498,985
Projection read83,000303,733712,451765,186767,919
Append with four readers1,4484,4088,84812,33013,427

SQLite's 100-event CCC workload peaked at 16 clients. Projection reads scaled through 256 clients, although the small gain beyond 16 readers came with a large increase in tail latency. Single-event CCC append remained close to unconditional append through 64 clients.

Tail latency​

Values are foreground request p99 latency in milliseconds.

Workload1 client4 clients16 clients64 clients256 clients
CCC append, 1 event1.172.253.6110.9240.42
CCC append, 100 events8.1618.8056.14240.37653.45
Unconditional append, 1 event2.231.974.418.8424.11
Conditional read7.828.887.7316.4381.36
Projection read43.0939.3154.85146.62599.55
Append with four readers5.184.817.2520.1649.08

During the mixed workload, the four background readers sustained 279,465, 287,727, 288,907, 276,100, and 262,568 events per second as foreground writer concurrency increased from 1 to 256.

Test environment​

  • Orisun v0.9.3-9-g53e5578, built as SQLite server and benchmark binaries with Go 1.26.5
  • SQLite WAL journal mode with synchronous=FULL
  • Apple MacBook Pro arm64, 10 logical CPUs, 32 GB RAM, host SSD
  • benchmark runner, Orisun server, and fresh per-boundary SQLite files on the host
  • 1,000,000 seed events, 100,000 indexed contexts, and approximately 256 bytes per event
  • 5-second warm-up and 30-second measurement per point

Download the raw SQLite CSV. These results describe the single-node SQLite backend; SQLite clustering is not supported.

FoundationDB results​

The FoundationDB results below use the same July 30, 2026 workload, client concurrency, dataset, payload, warm-up, and measurement duration. All 30 measured points completed with zero foreground and background request errors.

Throughput​

Values are events per second.

Workload1 client4 clients16 clients64 clients256 clients
CCC append, 1 event1866972,0243,9205,578
CCC append, 100 events8,47314,26720,01320,32318,503
Unconditional append, 1 event1607201,9463,8964,973
Conditional read9733,9459,80514,78520,421
Projection read49,900143,197235,730260,228264,529
Append with four readers1185041,5763,9556,303

The 100-event CCC workload saturated around 16 to 64 clients. Projection throughput continued to increase through 256 clients, but the final step added little throughput and substantially more latency.

Tail latency​

Values are foreground request p99 latency in milliseconds.

Workload1 client4 clients16 clients64 clients256 clients
CCC append, 1 event16.8016.1022.2453.93219.64
CCC append, 100 events55.13115.16302.971,436.643,271.17
Unconditional append, 1 event36.0918.0330.7268.58313.91
Conditional read30.1921.2449.83124.75278.63
Projection read52.62101.21203.87486.891,696.57
Append with four readers21.6317.3926.4344.3198.58

During the mixed workload, the four background readers sustained 180,167, 200,799, 170,998, 111,998, and 43,930 events per second as foreground writer concurrency increased from 1 to 256.

Test environment​

  • Orisun commit 53e5578, built as FoundationDB server and benchmark binaries with Go 1.26.5
  • FoundationDB 7.3.77 arm64 in Docker Desktop, with one fdbserver process
  • ssd-2 storage and log engines, single redundancy, and no machine fault tolerance
  • Apple MacBook Pro arm64, 10 logical CPUs, 32 GB RAM, host SSD
  • benchmark runner and Orisun server on the host; FoundationDB in local Docker
  • a fresh FoundationDB root and benchmark boundary
  • 1,000,000 seed events, 100,000 indexed contexts, and approximately 256 bytes per event
  • 5-second warm-up and 30-second measurement per point

Download the raw FoundationDB CSV. This is a single-process FoundationDB baseline. It does not represent a fault-tolerant FoundationDB cluster.

Default benchmark​

The default run pre-populates one clean boundary with 1,000,000 events spread over 100,000 indexed benchmark_context values. Events are approximately 256 bytes. Each point has a 5-second warm-up and a 30-second measurement at 1, 4, 16, 64, and 256 concurrent clients.

It covers six representative workloads:

WorkloadWhat it represents
Conditional append, 1 eventA normal command using CCC
Conditional append, 100 eventsA large command using CCC
Unconditional append, 1 eventAn ingestion-style write
Conditional readBuilding a decision model from a content query
Unconditional readRebuilding a projection
Append with four readersRead/write interference

Every point reports requests/second, events/second, request errors, and p50, p95, p99, and maximum latency. Mixed workloads also report the background read rate and errors.

Build the server​

Do not start the server with go run. Build a backend-specific binary with version, commit, and build-time metadata:

./build.sh \
"$(go env GOOS)" \
"$(go env GOARCH)" \
"$(git describe --tags --always)" \
pg

Start the generated binary in build/ with the production configuration you want to measure. Use sqlite instead of pg for the SQLite binary.

Release binaries and immutable release container images already contain the required build metadata.

Run​

Use a unique boundary and namespace so the runner starts with a clean dataset:

./collect_benchmarks.sh \
--address 127.0.0.1:5005 \
--boundary benchmark_20260730 \
--backend postgres \
--namespace benchmark_20260730 \
--create-boundary \
--label 'machine=MacBook Pro M4 Pro, 48 GB' \
--label 'database=PostgreSQL 18, synchronous_commit=on' \
--label 'topology=client, server, and database on one host'

collect_benchmarks.sh compiles the benchmark runner and then executes that binary. It never uses go run.

The run writes two portable files:

benchmark-results/<UTC timestamp>/
├── results.json
└── results.csv

results.json contains the full method, server and runner build metadata, machine information, optional labels, and results. results.csv is convenient for plotting.

The command exits non-zero if any foreground or background request fails, but still writes the results collected up to that point.

Smoke test​

Use the same compiled server binary and add --smoke:

./collect_benchmarks.sh \
--address 127.0.0.1:5005 \
--boundary benchmark_smoke_20260730 \
--backend postgres \
--namespace benchmark_smoke_20260730 \
--create-boundary \
--smoke

Smoke mode uses 1,000 seed events, two workloads, two concurrency points, and a one-second measurement. It checks the harness; its results are not suitable for performance claims.

Optional workloads​

The runner also includes conditional and unconditional append sizes of 1, 10, 100, and (for unconditional writes) 1,000 events, a rate-limited projection read, and read-with-writers. Run the complete set with:

./collect_benchmarks.sh --scenarios all <connection and boundary arguments>

Or select individual workloads:

./collect_benchmarks.sh \
--scenarios write-conditional-1,write-conditional-10,read-conditional \
--concurrency 1,8,32,128 \
<connection and boundary arguments>

Use ./collect_benchmarks.sh --help for all configuration flags.

Publishing results​

Keep the chart and its setup together. At minimum, state the Orisun version, backend and database version, durability settings, machine, storage, topology, dataset size, payload size, warm-up, measurement duration, and error count. The report captures most of this automatically; use repeatable --label arguments for the deployment details it cannot discover.

For comparisons, keep durability, consistency, dataset, payload, transport, and concurrency identical. If you need confidence intervals or run-to-run variance, repeat the same command and calculate them from the CSV files. The runner deliberately does not turn that publication workflow into another benchmark profile.