Skip to content

Measured, not promised.

Akter against six actor and workflow systems and two plain databases, on the same machine, under the same HTTP workload. Medians of three runs. Where Akter loses, it says so.

Sequential write p501.62 msone caller, one key
Hot key1,647op/s, 64 callers on one key
10,000 fresh keys791op/s, 64 callers
Fresh read p500.40 mscommitted state over HTTP

Machine. One Daytona sandbox, 4 CPUs and 8 GiB shared by every app, database and driver.

Systems. Rivet (two save modes), workerd local, Restate, Temporal, DBOS, plain Postgres, plain Redis.

When. 2026-10-01 to 02. Three rounds, forward, reverse, forward.

Each bar is the median of three rounds; the hairline beneath it spans the lowest to the highest round. Plain Postgres and Redis are printed beside each case, not drawn, because they skip what an actor adds.

Sequential write

One caller, one key. Successful acknowledged writes per second.

Akter532 op/s
workerd (local)391 op/s
DBOS252 op/s
Rivet default217 op/s
Restate205 op/s
Rivet saved145 op/s
Temporal106 op/s
Plain-store baselinesPostgres 3,673 · Redis 3,567 op/s

One hot key, 64 callers

Every caller writes to the same identity. Successful writes per second.

Akter1,647 op/s
Rivet default1,141 op/s
Rivet saved1,113 op/s
workerd (local)975 op/s
DBOS770 op/s
Restate368 op/s
Temporal2.6 op/s *
Plain-store baselinesPostgres 5,213 · Redis 24,293 op/s

* Temporal: 171 measured errors; the figure counts successful requests only.

10,000 keys, 64 callers

Writes spread across many fresh identities. Successful writes per second.

Restate1,627 op/s
Akter791 op/s
DBOS777 op/s
workerd (local)488 op/s
Temporal276 op/s
Rivet defaultnot collected
Rivet savednot collected
Plain-store baselinesPostgres 14,599 · Redis 22,153 op/s

Sequential read

Committed-state read through HTTP. Median latency, lower is better.

DBOS0.265 ms
Akter0.4 ms
workerd (local)1.693 ms
Temporal3.237 ms
Rivet default4.569 ms
Rivet saved4.759 ms
Restate5.178 ms
Plain-store baselinesPostgres 0.177 · Redis 0.121 ms

First command to a new key

Creating an identity while the process is running. Median latency, lower is better.

Akter2.018 ms
DBOS3.635 ms
workerd (local)4.272 ms
Restate5.025 ms
Temporal14.001 ms
Rivet saved77.868 ms *
Rivet default116 ms
Plain-store baselinesPostgres 0.246 · Redis 0.261 ms

* Rivet saved: 1 measured error; the figure counts successful requests only.

Wake after idle

First command after 15 s idle. Median latency, lower is better.

Akter1.807 ms
workerd (local)3.439 ms
Rivet defaultnot collected
Rivet savednot collected
Restatenot collected
Temporalnot collected
DBOSnot collected
NoteOnly measured where an actor can hibernate.
192,794

acknowledged IDs in the final crash and partition control

0lost
0duplicated
0unknown
0misrouted

Each drill injects four failures: killing the app, killing storage, or partitioning the network. Every acknowledged ID is read back afterwards.

RoundDrillAcknowledgedMissingRepeatedUnknown
clean1chaosApp52,3790016
clean1chaosStorage49,144000
cleanchaoschaosApp69,945000
cleanchaoschaosStorage72,798000
cleanchaospartitionApp70,623000
correctedfailurechaosApp72,551000
correctedfailurechaosStorage58,716000
correctedfailurepartitionApp61,527000

Where we lose

  • Plain Postgres and Redis were much faster. They skip actor lifecycle, receipts, events and the outbox.
  • Restate roughly doubled Akter's throughput across 10,000 keys.
  • DBOS's direct-SQL reads were faster than Akter's actor read path.
  • Cloud and isolate advantages, like Cloudflare's replication network, are not measured here.

The fine print

  • Same-host observations on one sandbox, not production SLOs.
  • Systems acknowledge at different durability boundaries; the report lists each one.
  • Missing or timed-out cases stay "not collected", never a pass.