Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content

Benchmarks

Event decode β†’ expression eval β†’ relayer hand-off are in-process function calls: no HTTP hop, no serialization on the hot path. This page puts numbers on that.

Headline numbers

The engine-side hot path, event claimed β†’ transaction queued in the embedded relayer, measured over 30 event-triggered sends:

Metricp50p95max
Claim β†’ tx queued (simulate + estimate gas + policy checks + relayer hand-off)25.1 ms34.3 ms39.2 ms
Claim β†’ run settled (full run, single send step, wait_for: none)28.1 ms37.3 msβ€”
Event's block timestamp β†’ claim (detection, polling a 1s-block chain)0.83 s0.91 s0.92 s

Those ~25 ms include real, journaled work: the exactly-once claim transaction in Postgres, template evaluation, a pre-flight eth_call simulation, an eth_estimateGas, policy/gas-cap checks, the pre-send journal write with the idempotency key, and the in-process relayer queue insert.

Methodology β€” reproduce it yourself

  • Setup: a release build of rflow (v0.1.0, cargo build --release), a local anvil chain (--block-time 1, chain id 31337) and the repo's compose Postgres, all on one machine.
  • Workflow: an ERC20 Transfer event trigger with a where: filter β†’ one send_transaction step (wait_for: none, simulation on, the default). The token-transfer-relay example minus the balance-gate read.
  • Load: 30 qualifying deposits via cast send; each produced exactly one run (n=30, all succeeded, zero duplicates).
  • Measurement: timestamps from the engine's own journal: workflow_runs.created_at (the claim) to step_runs.finished_at of the send step, which with wait_for: none settles at the relayer queue ack. Percentiles computed in SQL; detection latency compares the event's block timestamp to the claim time.
  • Hardware: Apple M5 Max, 128 GB RAM (a development laptop).
  • In-repo harness: cargo run -p rflow_e2e_tests -- --bench runs a self-contained variant (20 sequential deposits, instant-mining anvil, unoptimized dev build) and writes bench-results.json with every sample, the methodology, and on-chain receipt verification per tx. Its ~32 ms p50 claim β†’ send settled (dev build) is consistent with the table above.

Honest caveats

  • Local RPC. anvil answers eth_call/eth_estimateGas in microseconds; a real provider adds its round-trips to the ~25 ms (two RPC calls sit inside the measured window). The number isolates rflow's own overhead.
  • Detection is poll-bound in this setup. The 0.83 s p50 reflects block polling against a 1-second chain with 1-second timestamp granularity. Tightening block_poll_frequency (networks config) shrinks it; on real chains the block interval (12 s on mainnet) dominates. ws: is parsed but not wired to the engines yet, so it does not help here.
  • Queued, not confirmed. The relayer owns broadcast, gas bidding and inclusion. End-to-end "event β†’ CONFIRMED" is chain-time dominated (block interval Γ— your confirmations depth).
  • One machine, one process, modest n. n=30 on a dev laptop is a smoke-level benchmark, not a load test. Concurrency limits (max_concurrent_runs, group lanes) were not stressed.

Why no competitor comparison table?

Tenderly Web3 Actions and Gelato are hosted: their event→action latency includes detection infrastructure, queueing and multi-tenant scheduling that cannot be benchmarked in a controlled way. OpenZeppelin's Relayer + Monitor pair runs locally, but its workflow layer is whatever glue you write between the two services (Monitor webhook → your code → Relayer HTTP API), so any number would benchmark that glue. The architectural difference stands on its own: rflow's trigger-to-relayer path is a function call inside one process on your hardware, plus two RPC round-trips to your provider.

Where throughput actually goes

For capacity planning, the bottlenecks in practice, in order:

  1. Your RPC provider β€” simulation + gas estimation are two calls per send; backfills are eth_getLogs-bound (max_block_range, CU budgets).
  2. Postgres β€” every claim, step and settle is a journaled write. Give it real storage; it is the durability you are paying for.
  3. The chain itself β€” inclusion and confirmation depth; rflow just waits well (durably parked, not spinning).

See Self-hosting β†’ Sizing.