Skip to content

The Architecture: Edge-Native Gaming Authority

The core idea is simple: the lock is the object, the object is the database, and the database lives at the edge.

Instead of routing 10,000 concurrent bets through a single centralised database and praying the locks hold, we spawn a Durable Object per user. Each object is single-threaded, processes requests sequentially, and persists state to its own transactional store. There is no row-level lock because there is no contention—the object is the authority.

graph TD
    User([User / Load Gen]) -->|HTTPS| Worker(Main Worker)

    subgraph Cloudflare_Edge ["☁️ Cloudflare Edge"]
        subgraph Betting_Engine ["🎰 Betting Engine (Atomic)"]
            Worker -->|/api/bet| DO[Durable Object: BettingLedger]
            DO -->|Persist| Storage[DO Storage]
        end

        subgraph Compliance_Pipeline ["📜 Compliance (Async)"]
            Worker -->|Audit Log| Queue[Cloudflare Queue]
            Queue -->|Batch Consumer| LogWorker[Logger Worker]
            LogWorker -->|Batch Insert| D1[(D1: compliance_db)]
        end

        subgraph Game_Lobby ["🎮 Game Lobby (Global)"]
            Worker -->|/api/lobby?source=edge| KV[(Workers KV: GAME_LOBBY)]
            Worker -->|/api/lobby?source=origin| ExtAPI[External API / Simulation]
        end
    end

Three primitives, one codebase:

  1. Durable Objects — Atomic betting with zero race conditions.
  2. Queues & D1 — Non-blocking compliance logging.
  3. Workers KV — Edge-cached game metadata for global lobbies.

A BettingLedger Durable Object is spawned for each user via idFromName(userId). It holds the balance in memory and processes every request sequentially. There is no “check-then-act” race because no two requests can interleave.

  1. Authorise — Place a hold on the user’s balance. The DO sets a 30-second alarm. If the confirm never arrives, the alarm rolls the hold back automatically.
  2. Confirm — Atomically consume the hold and debit the balance. The bet is placed.

Every request carries a request_id. The DO caches the result of each operation for 24 hours. If a network retry replays a request, the DO returns the cached response instead of processing the transaction twice.


The UKGC mandates audit log persistence within 1 second of a transaction. In a traditional architecture, that means a synchronous INSERT into a central SQL database on the critical path—adding 200ms+ to every bet.

We decouple it entirely:

  1. Worker processes the bet via the Durable Object.
  2. Worker enqueues an audit message to Cloudflare Queues (fire and forget).
  3. Response is returned to the user in <50ms.
  4. A Consumer Worker pulls messages in batches and inserts them into D1.

If D1 is under write contention, messages stay in the queue with exponential backoff. After 3 retries, they move to a Dead Letter Queue (DLQ) for manual reconciliation. No audit data is ever lost.


The entire stack is defined in a single wrangler.jsonc:

{
"name": "igaming-edge-platform",
"main": "./src/worker.ts",
"durable_objects": {
"bindings": [{ "name": "BETTING_LEDGER", "class_name": "BettingLedger" }],
},
"queues": {
"producers": [{ "binding": "AUDIT_QUEUE", "queue": "casino-audit-logs" }],
"consumers": [{ "queue": "casino-audit-logs", "max_batch_size": 10 }],
},
"d1_databases": [
{ "binding": "DB", "database_name": "casino-compliance-db" },
],
"kv_namespaces": [{ "binding": "GAME_LOBBY", "id": "..." }],
}