Thing Event SystembyPentatonic
Blog/Edge-Native Event Processing
InfrastructureMarch 28, 20268 min read

Edge-native event processing with Cloudflare Workers

TES processes events in under 50ms from anywhere in the world. No cold starts, no region selection, no capacity planning. Here's how the architecture works — and why edge-native matters for event-sourced systems.

Why edge-native?

Traditional event stores run in a single region. If your users are in Tokyo and your event store is in us-east-1, every event incurs 200-300ms of network latency before processing even begins. For AI agents making real-time decisions, that latency compounds.

Edge-native means the event ingestion layer runs in 300+ locations globally. The event is received, validated, and acknowledged at the nearest edge node. Processing, enrichment, and projection happen asynchronously — the client doesn't wait for them.

The Cloudflare stack

TES uses four Cloudflare primitives, each chosen for a specific role:

  • WorkersRequest handling. Zero cold starts. The GraphQL API, event ingestion, and authentication all run as Workers. V8 isolates start in under 1ms.
  • D1Projection storage. SQLite-based, edge-replicated. Current state (projections) is stored in D1 with read replicas close to users. Writes go to the primary.
  • QueuesEvent processing. Events are queued for asynchronous processing — enrichment, embedding, projection updates. At-least-once delivery with batching.
  • R2Object storage. Images, documents, and event payloads stored in R2 with zero egress fees. CDN-backed for fast retrieval.

The event flow

When an event arrives at TES, the processing happens in two phases:

Phase 1: Synchronous (sub-50ms)

1. Worker receives request at nearest edge

2. Validate auth token + client ID

3. Validate event schema

4. Store event in D1 (append-only)

5. Enqueue for async processing

6. Return event ID to client

Phase 2: Asynchronous (~500ms)

7. Queue consumer picks up event

8. Update projections (current state)

9. Run AI enrichment pipeline

10. Generate/update embeddings

11. Deliver webhooks

The key insight: the client gets a response in sub-50ms. The event is safely stored. All the expensive work — vision analysis, embedding generation, projection updates — happens in the background. The system is optimistically consistent: projections lag events by ~500ms, but the event record is immediately durable.

Multi-tenant isolation

Every TES client gets its own D1 database. This isn't logical isolation with tenant IDs in a shared database — it's physical isolation. One client's data never exists in another client's storage. This simplifies compliance (EU data residency), eliminates noisy-neighbour problems, and makes client offboarding a database deletion.

Why not containers?

Container-based event stores (running on ECS, Kubernetes, or similar) have three problems for this workload: cold starts, region pinning, and scaling granularity.

V8 isolates on Cloudflare Workers have zero cold starts — every request is handled immediately. There's no region to choose — the code runs at the nearest location. Scaling is per-request, not per-instance — you don't pay for idle capacity.

The tradeoff is compute limits. Workers have a 30-second CPU time limit per request. That's why TES splits processing into synchronous (fast, bounded) and asynchronous (unbounded, queued) phases. Event ingestion never hits the limit. AI enrichment runs in queue consumers with their own budgets.

Performance numbers

MetricValue
Event ingestion (p50)12ms
Event ingestion (p99)45ms
Projection latency~500ms
Cold start0ms (V8 isolates)
Edge locations300+
AI enrichment (vision + pricing + embedding)2-4s (async)
Vector search (p50)35ms

These numbers are from production workloads, not benchmarks. The status page shows live uptime and response time data.

Pentatonic Engineering

London, UK

Create free account

Try it yourself

Sub-50ms event ingestion, globally

No region selection, no cold starts, no capacity planning. Start emitting events from anywhere.