Architecture Proposal

OpenRally Architecture v2: Protocol-Grade Infrastructure

Definitive tech stack for the OpenRally Protocol server. Rust (Axum) on Fly.io, Dragonfly for cache/pub-sub, Turso for edge reads, and Neon Postgres for primary writes. This is the best-in-class architecture — modeled after how Stripe, Cloudflare, and OTA build protocol infrastructure. The Setpoint admin app stays on its current stack — only the protocol server moves.

Recommendedv2.0Updated 2026-03-10

Definitive architecture recommendation — not a proposal, the answer.

This is not one option among many — it is the definitive recommendation. The v1 stack (Supabase/Vercel/Next.js) is architecturally incapable of running a protocol server: SSE breaks at 300s, serverless cold starts violate SLAs, and Node.js GC pauses make hold-creation latency non-deterministic. Every serious protocol and API infrastructure in production (Stripe, Cloudflare, OTA, Google Maps Platform) runs on compiled languages with always-on servers. This stack is the proven pattern.

Definitive tech stack for the OpenRally Protocol server. Rust (Axum) on Fly.io, Dragonfly for cache/pub-sub, Turso for edge reads, and Neon Postgres for primary writes. This is the best-in-class architecture — modeled after how Stripe, Cloudflare, and OTA build protocol infrastructure. The Setpoint admin app stays on its current stack — only the protocol server moves.

Why v1 Architecture Is Wrong for a Protocol Server

The v1 architecture was designed as an extension of the Setpoint admin app. A protocol server has fundamentally different requirements.

Requirementv1 StackProblem
Persistent connections (SSE)Vercel serverless, 300s maxSSE breaks every 5 minutes. Reconnect overhead.
Deterministic latencyNode.js single-threaded, GC pausesp99 spikes during garbage collection
Always-onServerless cold starts (500ms-2s)Unacceptable for Google Booking Server callbacks
Concurrent SSE~50 connections (serverless limits)Protocol needs 10,000+ per region
Cache layerNone definedEvery availability query hits database
Global distributionSingle region (Tokyo via Supabase)200ms+ latency for non-APAC consumers

Proposed Tech Stack

LanguageRust (Axum)

10-15x throughput vs Node.js. Sub-ms p99 latency. Zero GC pauses. Native async SSE via tokio. Serde for zero-copy JSON. SQLx for compile-time checked queries.

HostingFly.io

Always-on MicroVMs in 40+ regions. Anycast routing. Native long-lived connections. No cold starts. ~$0.0000573/s per shared CPU.

Primary DBNeon Postgres

Serverless Postgres with branching. Strong consistency for writes (holds, bookings). Connection pooling built-in. Scale-to-zero for dev.

Edge ReadsTurso (libSQL)

Embedded replicas — sub-1ms reads at edge. 26+ global locations. Perfect for availability queries. Replication lag < 100ms.

Cache + Pub/SubDragonfly

Multi-threaded Redis-compatible. 25x throughput over Redis. 60% better memory. Pub/Sub for SSE fan-out. Runs on Fly.io.

ObservabilityGrafana + OpenTelemetry

Distributed tracing, metrics, logs. Rust tracing crate → OTLP exporter.

Four-Layer Cache Architecture

L1

In-Process HashMap (DashMap)

Latency: ~1μsTTL: 5sScope: Per-instance

Hot paths: sport list, facility metadata

L2

Dragonfly

Latency: ~0.5msTTL: 30sScope: Per-region

Availability results, rate limits, sessions

L3

Turso Embedded Replica

Latency: ~5msTTL: Async replicationScope: Per-instance

Facility/resource/slot reads, discovery

L4

Neon Postgres

Latency: ~50msTTL: Source of truthScope: Global (single primary)

Booking writes, holds, event log

Invalidation: Write to L4 (Neon) → trigger emits event → Dragonfly Pub/Sub → invalidate L1 + L2 in all instances → Turso replicates automatically (<100ms)

SSE Push Architecture (v2)

Neon Write

DB trigger → or_events

NOTIFY

Postgres → Rust listener

Dragonfly

PUBLISH to channels

Axum SSE

Fan-out to 10K+ clients

10,000+

Concurrent connections/VM

Unlimited

Connection duration

<5ms

Event delivery

Performance: v1 vs v2

Operationv1 (Node/Vercel)v2 (Rust/Fly.io)Improvement
Availability query200-500ms<5ms (cache hit)40-100x
Availability (miss)200-500ms<50ms (Turso edge)4-10x
Hold creation300-800ms<10ms30-80x
Booking confirmation500-1500ms<50ms10-30x
SSE event deliveryBroken (300s limit)<5msFixed
Concurrent SSE~5010,000+ per VM200x
Cold start500ms-2s0ms (always-on)Eliminated
Throughput (req/s)~1,00050,000+50x

Infrastructure Cost Projections

Line-item costs based on official vendor pricing (Fly.io, Neon, Turso, Grafana Cloud). All prices in USD.

Tier 1Dev / Prototype

$4.02/mo

Facilities: 1Requests: <1K/day~$48/yr
Fly.io

1× shared-cpu-1x / 256MB (protocol server)

$2.02
Fly.io

Anycast IPv4

$2.00
Neon

Free tier (100 CU-hrs, 0.5GB)

Free
Turso

Free tier (500M reads, 5GB)

Free
Dragonfly

Skip (in-process cache only)

Free
Grafana

Free tier

Free
Tier 2Launch

$19.13/mo

Facilities: 1–10Requests: 1K–50K/day~$230/yr
Fly.io

1× shared-cpu-1x / 512MB (primary, Madrid)

$3.32
Fly.io

1× shared-cpu-1x / 256MB (Dragonfly)

$2.02
Fly.io

Anycast IPv4

$2.00
Fly.io

Volumes (1GB, Dragonfly snapshots)

$0.15
Fly.io

Bandwidth (~50GB/mo)

$1.00
Neon

Launch plan (~50 CU-hrs, 1GB storage)

$5.65
Turso

Developer plan

$4.99
Grafana

Free tier

Free
Tier 3Growth

$71.49/mo

Facilities: 10–100Requests: 50K–500K/day~$858/yr
Fly.io

2× shared-cpu-2x / 1GB (primary + replica)

$13.28
Fly.io

2× shared-cpu-1x / 256MB (Dragonfly ×2 regions)

$4.04
Fly.io

Anycast IPv4

$2.00
Fly.io

Volumes (2GB)

$0.30
Fly.io

Bandwidth (~200GB/mo)

$4.00
Neon

Launch plan (~200 CU-hrs, 5GB storage)

$22.95
Turso

Scaler plan

$24.92
Grafana

Free tier (still under limits)

Free
Tier 4Scale

$241.19/mo

Facilities: 100+Requests: 500K–5M/day~$2,894/yr
Fly.io

4× shared-cpu-2x / 1GB (2 primary + 2 replicas)

$26.56
Fly.io

3× shared-cpu-1x / 512MB (Dragonfly ×3 regions)

$9.96
Fly.io

Anycast IPv4

$2.00
Fly.io

Volumes (5GB)

$0.75
Fly.io

Bandwidth (~1TB/mo)

$20.00
Neon

Launch plan (~500 CU-hrs, 20GB storage)

$60.00
Turso

Scaler plan + overage (~50GB storage)

$37.92
Grafana

Pro ($19 base + ~20K series)

$84.00

Pricing basis: Fly.io shared-cpu-1x/256MB $2.02/mo, 512MB $3.32/mo, 2x/1GB $6.64/mo, IPv4 $2/mo, bandwidth $0.02/GB. Neon Launch $0.106/CU-hr + $0.35/GB storage. Turso Dev $4.99/mo, Scaler $24.92/mo. Grafana Free 10K series / 50GB logs, Pro $19 base.

Rust Crate Stack

axumHTTP framework (tower-based, async)
tokioAsync runtime
serde / serde_jsonZero-copy JSON serialization
sqlxCompile-time checked SQL (Postgres + SQLite)
redisDragonfly client (Redis-compatible)
libsqlTurso embedded replica client
tower / tower-httpMiddleware: rate limiting, auth, CORS, compression
axum-extraSSE support (Sse, Event)
schemarsGenerate JSON Schema from Rust types
tracing + tracing-opentelemetryStructured logging + distributed tracing
dashmapLock-free concurrent HashMap (L1 cache)
uuidUUID v7 generation (time-ordered)
chronoDate/time handling with timezone support

Deployment Topology

PrimaryMadridmad

2x shared-cpu-1x, 512MB + Dragonfly + Turso replica

ReplicaVirginiaiad

1x shared-cpu-1x, 256MB + Dragonfly + Turso replica

ReplicaTokyonrt

1x shared-cpu-1x, 256MB + Dragonfly + Turso replica

Anycast: All regions share one IP address. Traffic routes to the nearest healthy instance automatically.

Setpoint Integration

Setpoint Admin

Vercel / Next.js / Supabase

Stays on current stack

Adapter reads

OpenRally Server

Fly.io / Rust / Neon + Turso

New protocol-grade infra

Two deployment models: Standalone (own Neon DB) or Setpoint Adapter (reads from Supabase, writes to Neon)

Open Questions — Resolved

Slots: materialized or computed?

v1 Answer

Undecided

v2 Answer

Materialized in Turso — pre-compute into edge replicas, sub-1ms reads

SSE: Vercel or Supabase Edge?

v1 Answer

Vercel streaming (300s limit)

v2 Answer

Rust on Fly.io — unlimited connections, Dragonfly pub/sub fan-out

Facilities without schedules?

v1 Answer

Undecided

v2 Answer

Default schedule — 7am-11pm, 1hr slots, overridable per facility

Google Actions Center timing?

v1 Answer

Undecided

v2 Answer

Start now — v2 stack is Google-ready from day one

Decision Log (v2)

DecisionChoiceRationale
Separate protocol serverOwn binary, own infraProtocol server is infrastructure, not a web app feature. Different scaling, reliability, and latency requirements.
Rust over Node.jsRust (Axum)Deterministic latency. GC pauses unacceptable during hold creation. OTA runs Java/C++, Stripe core is Java, Cloudflare edge is Rust.
Fly.io over VercelFly.io MicroVMsAlways-on, Anycast routing, native SSE/WebSocket, 40+ regions, cheaper at scale.
Neon over Supabase (writes)Neon PostgresServerless Postgres with branching, connection pooling, no vendor lock-in.
Turso for edge readsTurso embedded replicasSub-1ms availability reads at edge. 90%+ of traffic is reads. In-process — no network hop.
Dragonfly over RedisDragonflySame API, 25x throughput (multi-threaded), 60% better memory. Doubles as SSE pub/sub.
4-layer cacheL1→L2→L3→L4Each layer serves a purpose. Invalidation via Dragonfly pub/sub propagates to all instances.
Setpoint stays on SupabaseNo migrationAdmin app is a CRUD interface — Supabase/Vercel/Next.js is fine. Only the protocol server needs protocol-grade infra.