El patrón fundamental para proteger D1 contra carga viral. Reads frecuentes con datos que cambian poco viven en KV. Cache-aside porque Worker orquesta — no es write-through.
Conocer las primitivas es el principio. Saber componerlas es la maestría. Este volumen recoge los patrones de composición, las leyes distribuidas que los gobiernan, y los sistemas canonical que prueban su validez.
Las recetas canonical para componer primitivas. Cada una nace de un problema concreto que ninguna primitiva sola resuelve bien.
El patrón fundamental para proteger D1 contra carga viral. Reads frecuentes con datos que cambian poco viven en KV. Cache-aside porque Worker orquesta — no es write-through.
El patrón canonical para counters virales. DO.increment() incrementa this.count en nanosegundos. Cada 10s, alarm dispara flush() que escribe el delta a D1. Single-writer guarantee del DO elimina race conditions.
Para transacciones distribuidas (charge → ship → notify) que cruzan servicios sin 2PC. Cada step persiste resultado. Si ship() falla, ejecutas refund() (la compensación de charge()). El runtime garantiza que cada step se ejecuta exactamente una vez gracias al replay.
Decouple compute pesado del request path. Worker recibe webhook, encola N tareas, responde 200 inmediato. Queue distribuye batches a consumers que pueden tardar minutos. Throughput escala con número de consumers.
Las queries de lectura son distintas a las de escritura. CQRS separa: writes serializan vía DO con strong consistency. reads consultan KV/D1 read-replicas con eventual consistency. La proyección de write a read state ocurre async vía Queue/Workflow.
DO recibe comando, valida, emite evento, lo append-onlyea a su SQLite. Estado actual = events.reduce(applyEvent, initialState). Replay reconstruye estado desde el log. Soporta time-travel debugging, audit log gratis, projections múltiples.
env.TENANT.idFromName(tenantId) garantiza aislación + single-writer.El patrón nativo de CF para multi-tenant. Tenant A nunca interfiere con Tenant B porque viven en DOs distintos. Crash de DO de A no afecta a B. Storage SQLite local per-DO = aislación de datos. Workers for Platforms va más lejos: cada tenant deploya su propio Worker bajo tu control.
El patrón canonical para AI agents serios. Vectorize recupera contexto semántico relevante. AI Gateway cachea respuestas idénticas (40% hit ratio típico) y mide costos. Workers AI o LLM externo genera respuesta. Si la respuesta es código, Sandbox SDK lo ejecuta en aislamiento. Workflow envuelve todo si es multi-turn con state durable.
Las leyes que gobiernan por qué cada primitiva existe. No las inventaron en Cloudflare — las descubrieron en los 70s-80s, y todo sistema serverless las honra.
"En presencia de partición de red, un sistema distribuido elige entre consistencia y disponibilidad. No las tres juntas." — Eric Brewer, 2000
Cuando dos nodos pierden contacto entre sí (partición), el sistema enfrenta un dilema: aceptar writes sólo en uno de ellos (CP, sacrificando disponibilidad en el otro) o aceptar writes en ambos (AP, sacrificando consistencia momentánea). CP y AP son las únicas opciones bajo partición.
Aplicación al stack: D1 es CP (single primary, partición → writes detenidos en replica). KV es AP (cualquier PoP acepta writes, propagan eventualmente). DO es CP per-ID (single-writer global, partición → DO espera). Saber esto explica por qué KV puede mostrar valores stale 60s después de un write — es la elección consciente AP.
"Si Partition (P), elige Availability (A) o Consistency (C). Else (E), elige Latency (L) o Consistency (C)." — Daniel Abadi, 2010
CAP sólo describe partición. PACELC añade: fuera de partición, sigue habiendo tradeoff entre latencia y consistencia. Para responder rápido, replicas leen sin consultar primary (consistency lag). Para consistencia fuerte, esperan al primary (latency).
Por eso D1 read replicas pueden retornar datos ~10s atrasados respecto al primary. No es bug — es la elección PACELC: prefieren latencia baja en lectura sobre frescura absoluta. KV es PA/EL (eventual + low latency). DO es PC/EC (strong + sacrifica latencia para garantizar serialización).
"Una operación es idempotente si ejecutarla N veces produce el mismo resultado que ejecutarla una vez."
Queue garantiza at-least-once delivery. Esto significa que tu consumer puede recibir el mismo mensaje 2-N veces. Si tu handler hace charge_credit_card(amount) sin idempotency key, cobrarás 2-N veces.
Solución: cada operación incluye un idempotency key único. El consumer guarda keys procesados (en KV con TTL, o D1). Si el key ya fue visto, retorna el resultado cacheado sin reejecutar. Esta ley aplica a Workflows (steps deben ser idempotentes para replay), Queues (retry), DO RPC (network reintentos). Sin idempotencia, los sistemas distribuidos corrompen estado silenciosamente.
"En sistemas distribuidos, no existe un 'ahora' global. Sólo existe el orden causal entre eventos." — Leslie Lamport, 1978
Dos eventos en máquinas distintas no pueden compararse por reloj físico (clocks drift). Lamport propuso: cada nodo lleva un contador local, lo incrementa en cada evento, lo envía con cada mensaje. Recipiente toma max(local, recibido) + 1.
Resultado: orden parcial entre eventos relacionados causalmente, sin necesidad de reloj global. Implicación práctica: cuando coordinas DOs distintos, no asumas orden temporal estricto entre sus operaciones — sólo el orden establecido por mensajes explícitos. CRDTs (próximo teorema) usan vector clocks (Lamport extendido) para resolver merges.
"Estructuras de datos que pueden ser replicadas y modificadas concurrentemente sin coordinación, garantizando convergencia eventual."
Ciertas operaciones se mergean automáticamente sin conflict: increment counters, add-only sets, last-write-wins registers. Si N replicas modifican concurrent y luego sincronizan, todas convergen al mismo estado sin lock.
KV no implementa CRDTs nativamente — por eso increment falla. DO sí soporta esta lógica si tú la implementas (single-writer hace CRDT trivial). Yjs y Automerge son CRDTs populares para colaboración real-time. Si tu producto es colaborativo (docs, whiteboards), CRDT + DO + WebSockets es la receta.
"Alternativa a ACID para sistemas distribuidos: tolerar inconsistencia momentánea a cambio de disponibilidad."
ACID (Atomic, Consistent, Isolated, Durable) es la base de DBs tradicionales — Postgres, MySQL. BASE relaja consistency: el sistema siempre responde (BA), el state puede cambiar sin input directo (S — propagación), eventualmente todos los nodos convergen (E).
KV es BASE puro. DynamoDB también. D1 intenta ACID (vía SQLite). DO es ACID per-instance (single-writer simplifica). Saber qué primitiva opera bajo qué modelo determina cómo razonas sobre tu state. Mezclar mental models ACID con sistemas BASE es la fuente #1 de bugs distribuidos.
"Atomicity distribuida vía coordinación bloqueante."
2PC fue el approach clásico: un coordinator pregunta a todos los participants "¿pueden commit?", luego envía "commit" si todos dijeron sí. Problema: si coordinator crashea entre fases, participants quedan bloqueados indefinidamente. No escala.
Saga (Pattern III) reemplaza 2PC en sistemas modernos. En vez de bloquear hasta confirmar, ejecuta steps secuenciales con compensaciones. Ofrece eventual atomicity en vez de strong atomicity. Cloudflare nunca ofreció 2PC porque no escala — Workflow es la implementación canonical de Saga en CF.
Arquitecturas canonical que ejercitan los patrones y teoremas. Si dominas estas, dominas el stack.
/r/promo. Worker tRPC recibe.link:promo. Hit (99%) → resuelve. Miss → consulta D1 + populate KV con TTL 1h.cf.country, aplica regla de redirect específica del país, devuelve 302.env.COUNTER.idFromName(slug).increment(). DO acumula in-memory.idFromName(roomId)). Patrón Multi-tenant (VII).conversationId.El árbol de decisión completo. Cuando enfrentas un nuevo requirement, recorrelo de arriba a abajo.
Los siete pecados arquitectónicos que cometen quienes no leyeron este codex. Evítalos.
Date.now(), Math.random(), o llamadas externas no-cacheadas dentro de un step. El castigo: replay genera valores distintos, state divergente, bugs imposibles de reproducir. La penitencia: capturar valores no-determinísticos en su propio step.El arquitecto novato pregunta "¿qué primitiva uso?". El arquitecto formado pregunta "¿qué tradeoff domina mi requirement?" — y la primitiva se vuelve consecuencia mecánica.
Las primitivas son notas. Los patrones son escalas. Los teoremas son armonía. Los sistemas de referencia son sinfonías ya escritas. Construir arquitectura es componer música — no copiar partituras.
El estudio nunca termina. Cada año Cloudflare añade primitivas (Containers, Sandbox, AutoRAG) y cada nueva primitiva expande el espacio de composiciones posibles. Mantén el codex vivo: revísalo trimestralmente, contrasta tus diseños contra los siete pecados, y mide tus elecciones por sus tradeoffs reales — no por su elegancia aparente.