wandres.dev
NIVEL DIOS: SÍNTESIS · la plataforma completa

El modelo mental completo: los dos ejes del edge

Treinta y dos niveles de plataforma se reducen, al final, a una sola tensión: acercarse al usuario o acercarse a los datos. Cada pieza de Cloudflare es una respuesta a esa tensión y cada arquitectura es una posición concreta en ese plano. Vemos por qué la latencia no es distancia sino distancia multiplicada por número de viajes, por qué Smart Placement es el mando físico que mueve el compute a lo largo del eje, por qué los Durable Objects rompen el dilema convirtiendo el dato en un lugar, dónde cae la IA en el mapa, y cómo recorrer una petición real de punta a punta nombrando en cada salto qué eje manda.

⏱ 24 min

Has recorrido el catálogo entero: isolates, bindings, KV, R2, D1, Durable Objects, Queues, Hyperdrive, Workers AI, Vectorize, Containers, Workflows. Cada pieza tenía su lógica interna y su lección. Este nivel no añade ninguna pieza nueva; hace algo más difícil y más útil, que es enseñarte a ver las treinta como respuestas distintas a una única pregunta. Esa pregunta no es cuál es más rápida ni cuál es más barata, sino dónde debe ocurrir el cómputo respecto a dos puntos que casi nunca coinciden: el usuario que espera y el dato que decide. Toda la plataforma cabe en ese plano, y quien lo ve deja de elegir productos y empieza a elegir posiciones.

🎯 Al terminar esta lección sabrás
  • Reducir la plataforma entera a dos ejes y situar cada pieza en el plano que forman.
  • Entender que la latencia percibida es distancia multiplicada por número de viajes, no distancia.
  • Reconocer los tres movimientos posibles: mover el compute, mover el dato o eliminar el viaje.
  • Recorrer una petición completa nombrando en cada salto qué eje manda y por qué.

Los dos ejes y la tensión que los une

El primer eje es el que vendió la plataforma y el que todo el mundo entiende: cerca del usuario. Un Worker no vive en una región, vive en cientos de puntos de presencia, y el navegador que lo invoca habla con el más cercano. El primer byte sale en milisegundos porque no hay cold start, no hay balanceador regional, no hay travesía continental antes de empezar a pensar. Ese eje es el que explica los assets estáticos, la Cache API, la lectura caliente de KV, el HTMLRewriter y el streaming de respuestas: todos son formas de terminar la petición sin salir del punto de presencia.

El segundo eje es el que la práctica descubre después, casi siempre con dolor: cerca de los datos. En cuanto tu petición necesita consultar una base relacional, coordinar una entidad o invocar un modelo, aparece un segundo punto en el mapa que no está donde el usuario. Y ahí la proximidad al usuario, que era una virtud, se vuelve un pasivo: si el compute está en Santiago y la primaria en Fráncfort, cada consulta cruza el planeta, y no una vez.

La tensión entre los dos ejes se formula con una aritmética que conviene tener grabada:

// Lo que el usuario percibe no es una distancia, es una suma de viajes.
const latenciaPercibida = rttUsuarioCompute + nViajes * rttComputeDatos;

// Con n = 1 el eje del usuario domina y conviene estar cerca de el.
// Con n = 6 el eje de los datos domina y estar cerca del usuario cuesta caro.

El término que casi nadie mira es nViajes, y es el que decide. Con una sola consulta, colocar el compute junto al usuario es obviamente correcto: pagas un viaje largo y lo compensas con un primer byte instantáneo. Con seis consultas encadenadas —comprobar sesión, cargar perfil, resolver permisos, leer el recurso, registrar la visita, refrescar un contador—, pagas seis veces la travesía y la cercanía al usuario se convierte en el peor sitio posible para ejecutar. La regla que se deriva de aquí es incómoda porque contradice el eslogan: el edge no siempre gana; gana cuando nViajes es pequeño.

Hay un matiz que la fórmula esconde y que conviene explicitar: los viajes en serie y los viajes en paralelo no cuestan lo mismo. Seis consultas independientes lanzadas a la vez cuestan un viaje de reloj, no seis, siempre que respetes el techo de seis conexiones simultáneas. Seis consultas donde cada una necesita el resultado de la anterior cuestan seis, sin remedio posible. Por eso la primera optimización de cualquier ruta lenta no es cambiar de plataforma ni mover el compute, sino romper la cadena de dependencias: mirar qué consultas dependen de verdad unas de otras y cuáles se encadenaron por comodidad de escritura.

Existe además un tercer eje, más discreto, que no es espacial sino temporal: la tolerancia al desfase. Un mismo dato colocado en el mismo sitio se comporta de forma completamente distinta según cuántos segundos de antigüedad aceptes. Si aceptas sesenta, la petición no viaja nunca porque la caché responde; si no aceptas ninguno, viajas siempre. Ese eje no aparece en los diagramas de arquitectura y sin embargo es el que más veces convierte un sistema lento en uno rápido, porque no requiere mover nada: requiere decidir, hecho por hecho, cuánta frescura necesita de verdad.

flowchart TD
A[cada pieza responde a un eje] --> B[cerca del usuario]
A --> C[cerca de los datos]
B --> B1[assets estaticos y Cache API]
B --> B2[KV en lectura caliente]
B --> B3[HTMLRewriter y streaming]
C --> C1[D1 primaria e Hyperdrive]
C --> C2[Workers AI junto a las GPU]
C --> C3[R2 con jurisdiccion elegida]
A --> D[Durable Objects rompen el dilema]
D --> D1[el dato es el lugar y el compute vive con el]
A --> E[Smart Placement mueve el compute por el eje]

Los tres movimientos posibles

Ante esa tensión solo existen tres jugadas, y toda la plataforma es una colección de herramientas para ejecutar una de las tres.

El primer movimiento es mover el compute hacia los datos. Eso es exactamente Smart Placement: no un truco de optimización, sino el mando físico que desplaza tu Worker a lo largo del eje. Cloudflare observa el patrón de subrequests y, si detecta que la ejecución pasa la mayor parte del tiempo esperando a un origen concreto, empieza a ejecutar el Worker cerca de ese origen en vez de cerca del usuario. Pagas un viaje largo de ida y vuelta al principio y ahorras nViajes menos uno. Es rentable precisamente cuando el término que domina es el segundo, y es contraproducente cuando el Worker responde sin salir a ningún sitio.

// Un unico interruptor que decide en que extremo del eje se ejecuta el Worker.
{
  "name": "api",
  "main": "src/index.ts",
  "placement": { "mode": "smart" }
}

Ese interruptor tiene una asimetría importante: no siempre acierta, y cuando falla lo hace hacia el lado que más se nota. Si tu Worker mezcla rutas —unas que responden desde la caché sin salir y otras que consultan seis veces al mismo origen—, la colocación es una sola para todas, así que optimizar las lentas penaliza a las rápidas. La respuesta madura ahí no es renunciar al mando, sino separar los Workers por perfil de acceso: uno en el borde para lo que se resuelve cerca del usuario, otro colocado junto a los datos para lo que no, y un service binding entre ambos que no cuesta una travesía de red porque no es red.

El segundo movimiento es mover los datos hacia el usuario. Es lo que hacen KV replicando lecturas a cada punto de presencia, las réplicas de lectura de D1, el caché de consultas de Hyperdrive y la Cache API guardando respuestas ya construidas. Este movimiento tiene un precio que se paga siempre en la misma moneda: consistencia. Un dato que está en trescientos sitios a la vez no puede estar actualizado en los trescientos en el mismo instante, y por eso KV es eventual y por eso la Sessions API de D1 existe: para que, al menos, cada sesión lea sus propias escrituras aunque el resto del mundo vaya unos milisegundos por detrás.

Conviene notar que este segundo movimiento es profundamente asimétrico entre lecturas y escrituras, y ahí se esconde el error más común. Replicar acerca la lectura a todo el mundo pero no acerca la escritura a nadie: escribir sigue implicando alcanzar un punto de verdad y esperar a que la propagación llegue. Un sistema que lee mucho y escribe poco gana enormemente; uno que escribe casi tanto como lee gana poco y paga toda la complejidad de la consistencia eventual. Antes de replicar cualquier cosa, la métrica que decide es la proporción entre lecturas y escrituras de ese hecho concreto, no la del sistema en conjunto.

El tercer movimiento es el más elegante y el que más se olvida: eliminar el viaje. Una respuesta cacheada no viaja. Un token verificado con criptografía local no consulta a nadie. Un ctx.waitUntil mueve el trabajo fuera del camino crítico, de modo que el viaje sigue existiendo pero el usuario ya no lo espera. Una cola convierte seis operaciones síncronas en una escritura y un procesamiento diferido. Este movimiento no negocia con la física: la anula.

El caso de la sesión merece detenerse porque es el ejemplo canónico y el que más se hace mal. Comprobar quién eres consultando una tabla de sesiones es un viaje por petición, en todas las peticiones, para todos los usuarios; comprobarlo verificando la firma de un token es aritmética local sobre bytes que el propio cliente trajo. La diferencia entre las dos arquitecturas no es de milisegundos: es que una escala con el número de peticiones y la otra no escala con nada, porque no toca ningún sistema compartido. Lo que se pierde es la revocación inmediata, y ahí está la transacción honesta, que se resuelve con vidas cortas y una lista de revocados en KV en lugar de con una consulta por petición.

Movimiento Herramienta típica Lo que pagas
Mover el compute Smart Placement, Containers Latencia del primer salto
Mover el dato KV, réplicas de D1, Cache API Consistencia eventual
Eliminar el viaje Cache, waitUntil, Queues, JWT Complejidad y frescura
💡
Mide `nViajes` antes de mover nada

Antes de activar Smart Placement, de meter una caché o de rediseñar el modelo de datos, cuenta los fetch y las consultas que hace una petición típica de tu ruta más caliente. Si el número es uno, el problema no es la colocación y moverla no arreglará nada. Si es seis, tienes tres opciones y las tres funcionan: acercar el compute, replicar el dato o fusionar las seis en una. La medición es barata y ahorra semanas de optimizaciones dirigidas al término equivocado de la suma.

Durable Objects y la IA: los dos casos que rompen el plano

Hay una pieza que no se deja colocar en el plano porque cambia sus reglas: el Durable Object. Las demás asumen que el dato está en un sitio y el compute en otro, y negocian la distancia entre ambos. Un Durable Object fusiona los dos puntos: el estado y el código que lo manipula viven en la misma máquina, se ejecutan de uno en uno y no hay travesía entre pensar y escribir. La distancia interna es cero por construcción, y lo que queda es un único viaje desde el usuario hasta esa entidad concreta.

Eso convierte la pregunta de la colocación en una pregunta de identidad: ya no es dónde ejecuto, sino qué entidad es la dueña de este hecho. Una sala de chat, un carrito, un documento colaborativo, una cuota por usuario, un agente conversacional. Elegir bien el idFromName es elegir bien la unidad de coordinación, y una unidad demasiado gruesa —un objeto por aplicación— serializa el mundo entero en un hilo, mientras que una demasiado fina no coordina nada.

// La granularidad del nombre es la decision de arquitectura, no un detalle de sintaxis.
const porTienda = env.STOCK.idFromName(tiendaId);            // serializa toda la tienda
const porProducto = env.STOCK.idFromName(`${tiendaId}:${sku}`); // serializa solo lo que compite
const stub = env.STOCK.get(porProducto);
await stub.reservar(unidades);

La regla que resuelve casi todos los casos es que el nombre del objeto debe coincidir con el conjunto mínimo de cosas que tienen que ordenarse entre sí. Dos compras del mismo producto compiten; dos compras de productos distintos no compiten en absoluto, y meterlas en el mismo objeto es inventar una contención que el dominio no tenía. Y aunque un Durable Object colapsa la distancia interna a cero, no elimina la externa: el usuario sigue teniendo que llegar hasta donde vive esa entidad, así que la latencia de un sistema en tiempo real depende de dónde se instanció el objeto la primera vez, que es una decisión que se toma sola si no la tomas tú.

La IA cae en el segundo eje con más fuerza que ninguna otra pieza, y esto sorprende a mucha gente. Un modelo no se replica a trescientos puntos de presencia porque una GPU no es un fichero: es hardware escaso, caro y concentrado. Cuando llamas a env.AI.run, tu petición viaja a donde hay capacidad de inferencia, y el tiempo que tarda el modelo en generar tokens es órdenes de magnitud mayor que cualquier latencia de red que puedas ahorrar. Por eso, en una aplicación de IA, optimizar la colocación del Worker es casi siempre optimizar el término irrelevante de la suma. Lo que sí mueve la aguja es el streaming, que empieza a entregar tokens antes de terminar de generarlos y convierte una espera de ocho segundos en una respuesta que arranca en trescientos milisegundos.

⚠️
La cercanía al usuario no es gratis si el trabajo real está lejos

El error arquitectónico más caro que se comete en esta plataforma es asumir que ejecutar en el edge mejora todo por definición. Un Worker junto al usuario que abre seis conexiones a una base en otro continente es medible y sistemáticamente más lento que el mismo código ejecutándose en un servidor pegado a esa base. El edge no derogó la velocidad de la luz; lo que hizo fue darte controles para colocarte donde convenga. Usarlos requiere saber dónde está el trabajo real, y eso solo lo dicen los números.

El recorrido completo de una petición

Vale la pena seguir una petición real y nombrar, en cada salto, qué eje manda. Un usuario en Lima pide la página de un producto en una tienda global.

El navegador resuelve el dominio y la petición aterriza en el punto de presencia de Lima. Aquí manda el eje del usuario y el trabajo consiste en no salir de ahí: el Worker consulta la Cache API con una clave que incluye el idioma y la moneda, y si acierta, la página se devuelve sin un solo viaje adicional. nViajes vale cero y la discusión ha terminado.

Si falla, empieza la parte interesante. El Worker lee de KV la configuración de la tienda —qué banners están activos, qué características se han desplegado— porque es un dato que se lee millones de veces, cambia poco y tolera ir unos segundos por detrás. Ese acceso no sale de Lima. Después necesita la ficha del producto con su precio y su stock, que vive en D1; aquí manda el eje de los datos, y la lección aprendida es leer de la réplica más cercana salvo que el usuario acabe de escribir, en cuyo caso la Sessions API garantiza que lea su propia escritura. Las imágenes son bytes grandes y salen de R2, servidas por su propia URL sin pasar por el Worker, porque hacer que un isolate haga de tubería de megabytes es desperdiciar el término barato de la suma.

El contador de unidades reservadas no puede vivir en ninguno de esos sitios: dos compras simultáneas del último ejemplar tienen que ordenarse, y para eso hay un Durable Object por producto. El registro analítico de la visita no debe retrasar la respuesta y se envía con ctx.waitUntil a un Queue, que lo procesará después en lotes. Y si la página incluye una recomendación generada, esa llamada viaja a donde están las GPU y se entrega en streaming, empezando por el texto que ya está listo.

Puesto en una tabla, el recorrido enseña algo que en prosa se diluye: cada salto obedece a un eje distinto y ninguno de los seis se decidió por el mismo motivo.

Salto Pieza Eje que manda Por qué
Página cacheada Cache API Usuario Cero viajes si acierta
Configuración KV Usuario Se lee mucho, cambia poco
Ficha y precio D1 con réplicas Datos Consulta sobre un conjunto
Imágenes R2 Ninguno No pasa por el isolate
Reserva de stock Durable Object Colapsado Exige orden estricto
Analítica Queue con waitUntil Eliminado El usuario no lo espera

Lo que hace útil ese ejercicio no es el resultado, que además cambia con cada producto, sino la disciplina de nombrar el eje en cada salto. Una arquitectura donde todos los saltos responden al mismo eje casi siempre está mal repartida: o bien se ha metido en la caché algo que necesitaba verdad, o bien se está consultando la base de datos para cosas que llevaban meses sin cambiar.

🧭

Dos ejes, no un eslogan

Toda decisión de arquitectura en esta plataforma es una posición entre la cercanía al usuario y la cercanía a los datos. No hay una respuesta universal, hay una posición correcta por ruta.

🔁

Cuenta los viajes

La latencia percibida es distancia por número de viajes. El término que casi nadie mide es el número, y es el que decide qué movimiento conviene.

🔱

Tres jugadas y solo tres

Mover el compute, mover el dato o eliminar el viaje. Todo el catálogo de Cloudflare es una colección de instrumentos para ejecutar una de las tres.

La plataforma no es un catálogo de productos, es una topología con mandos

Lo que separa a quien ha memorizado Cloudflare de quien lo ha entendido es dónde pone el sujeto de la frase. El primero dice que KV es rápido, que D1 es relacional y que los Durable Objects sirven para coordinar; son afirmaciones ciertas y estériles, porque describen productos aislados y no explican ninguna decisión. El segundo dice que en su ruta de checkout hay cuatro viajes al mismo origen y que por eso mueve el compute, o que su configuración se lee un millón de veces y cambia dos veces al mes y por eso la replica aceptando desfase, o que dos compras del último ejemplar tienen que ordenarse y por eso hay una entidad con un solo hilo. La diferencia es que el segundo habla de su propio sistema y usa los nombres de los productos como consecuencias, no como premisas. Y hay una razón profunda para que esa sea la forma correcta de pensar: la plataforma no te ofrece treinta soluciones a treinta problemas, te ofrece mandos sobre una única variable física que es la distancia entre el cómputo y el estado, más una única variable lógica que es cuántas veces hay que recorrerla. Todo lo demás son envoltorios sobre esas dos. KV es replicación agresiva pagada en consistencia. Hyperdrive es un pool de conexiones y una caché que reducen nViajes sin tocar tu base. Smart Placement es literalmente un deslizador entre los dos extremos del eje. Un Durable Object es la decisión de colapsar el eje a cero para una entidad concreta. Workers AI es la admisión de que hay cómputo que no se puede replicar y hay que ir a buscarlo. Cuando ves la plataforma así, dejas de preguntarte qué producto usar —una pregunta que se contesta con modas— y empiezas a preguntarte dónde está el trabajo real de esta ruta y cuántas veces la cruzo, que se contesta con mediciones y admite una sola respuesta honesta. Ese cambio de pregunta es, literalmente, todo lo que este nivel pretende dejarte.

⚔️ Sitúa tu propio sistema en el plano
  1. Elige la ruta más caliente de un proyecto que conozcas y cuenta exactamente cuántos viajes de red hace una petición típica.
  2. Decide cuál de los tres movimientos —mover el compute, mover el dato, eliminar el viaje— aplicaría mejor y justifica por qué los otros dos no.
  3. Identifica un dato de ese sistema cuya colocación sea incorrecta hoy y di qué eje se está pagando de más.
  4. Encuentra una entidad que necesite coordinación y argumenta qué granularidad de idFromName le corresponde.
  5. Explica por qué en una ruta dominada por inferencia optimizar la colocación del Worker es optimizar el término equivocado.