Diseñar dentro de los límites
Los límites del Worker no son muros contra los que estrellarse, sino señales que te dicen cuándo el trabajo pertenece a otra pieza. Cuando el cómputo desborda el techo de CPU, cuando hace falta durabilidad frente a fallos, cuando hay que orquestar muchos pasos o ejecutar código nativo, el Worker deja de ser el lugar y pasa a ser el recepcionista. Vemos los patrones para trabajo pesado —Queues para descargar, Workflows para orquestar con reintentos, Containers para salir del isolate— y el estilo que los une: el Worker como puerta rápida que delega.
Todo lo anterior de este nivel describía muros: tiempo de CPU, memoria, subrequests, tamaño. La tentación es tratarlos como enemigos y buscar cómo esquivarlos. Pero la lectura madura es la contraria: cada límite es una señal que te dice que ese trabajo, tal como lo planteas, no pertenece a un Worker de petición. La plataforma no te pide que hagas caber un elefante en una caja pequeña, sino que reconozcas cuándo el elefante necesita otra pieza —una cola, un flujo durable, un contenedor— y dejes al Worker hacer lo que hace de maravilla: recibir la petición y decidir a quién se la pasa.
- Reconocer las señales de que un trabajo no cabe en una invocación de Worker.
- Usar
Queuespara aceptar rápido y procesar en segundo plano con su propio presupuesto. - Usar
Workflowspara orquestar procesos de varios pasos con durabilidad y reintentos. - Recurrir a
Containerscuando necesitas un runtime, un binario nativo o recursos que el isolate no da.
Señales de que el trabajo no cabe
Antes de elegir una herramienta, aprende a leer los síntomas. Cada uno apunta a una carencia distinta del modelo de petición, y confundirlos lleva a la solución equivocada.
Desbordas la CPU
El cálculo es legítimamente largo y roza o supera el techo de cinco minutos de CPU. No es un bug: es trabajo que necesita más presupuesto o troceo.
Necesitas durabilidad
Si a mitad del proceso algo falla, no puedes permitirte repetirlo todo desde cero. Quieres que lo ya hecho quede hecho y solo se reintente lo que falló.
Orquestas muchos pasos
El trabajo es una secuencia de etapas con esperas largas entre ellas —minutos, días— y cada una con su propia política de reintento.
Necesitas código nativo
Requieres un binario, un runtime concreto, un sistema de ficheros completo o memoria y CPU que el isolate no ofrece. El modelo de lenguaje se te queda pequeño.
La respuesta a las tres primeras señales vive dentro del ecosistema de Workers; la cuarta te saca del isolate por completo. El hilo conductor es siempre el mismo: sacar el trabajo pesado de la ruta de la petición para que el usuario reciba una respuesta rápida y el trabajo ocurra donde tiene sitio.
Descargar con Queues, orquestar con Workflows
Queues resuelve la descarga. Tu Worker actúa de productor: recibe la petición, valida, mete un mensaje en la cola y responde de inmediato con un 202. El trabajo de verdad lo hace un consumidor, un Worker aparte que procesa los mensajes en segundo plano, por lotes, con reintentos, y con un presupuesto de CPU mucho mayor —hasta quince minutos por invocación—.
export default {
async fetch(request, env): Promise<Response> {
const trabajo = await request.json();
// acepta rapido y encola: el usuario no espera al trabajo pesado
await env.MI_COLA.send(trabajo);
return new Response("aceptado", { status: 202 });
},
};
Esa indirección compra tres cosas a la vez: desacopla la latencia del usuario del coste del trabajo, absorbe los picos de carga en un buffer, y da reintentos gratis cuando el procesamiento falla. Es el patrón para todo lo que puede ocurrir después: enviar correos, generar miniaturas, propagar cambios.
Workflows resuelve la orquestación. Cuando el trabajo no es un mensaje suelto sino una secuencia de pasos que debe sobrevivir a fallos, un Workflow describe esos pasos con step.do(). Cada paso que termina queda grabado para siempre y no se repite aunque el flujo se reinicie; los que fallan se reintentan con backoff; el estado sobrevive a caídas de infraestructura; y un flujo puede dormir esperando un evento durante hasta un año sin consumir recursos.
La pregunta no es “¿esto es grande?”, sino “¿qué forma tiene?”. Si es una tarea suelta que puede esperar, es una cola. Si es una cadena de pasos donde la mitad hecha debe conservarse, es un Workflow. Si es un cálculo único e intenso que solo necesita más CPU, quizá baste con subir cpu_ms. La forma manda sobre el tamaño.
Salir del isolate con Containers
Hay trabajo que ninguna cola ni flujo arregla, porque el problema no es el tiempo ni la durabilidad sino la naturaleza del cómputo: necesitas ffmpeg, un runtime que no compila a WebAssembly, un sistema de ficheros completo o varios núcleos de CPU en paralelo. Para eso están los Containers: ejecutan una imagen de contenedor —cualquier lenguaje, cualquier runtime— dentro de una VM Linux, controlada desde tu Worker.
import { Container, getContainer } from "@cloudflare/containers";
export class Render extends Container {
defaultPort = 8080;
sleepAfter = "10m"; // duerme el contenedor tras 10 min sin trafico
}
export default {
async fetch(request, env) {
// el Worker enruta a un contenedor que corre el binario pesado
return getContainer(env.RENDER, "sesion-1").fetch(request);
},
};
El detalle revelador es que la clase Container extiende a un Durable Object: el objeto gestiona el enrutado, el estado y el ciclo de vida, mientras el proceso pesado corre dentro de la VM. No escribes operadores de Kubernetes ni encadenas llamadas de API: describes el contenedor en el manifiesto y lo invocas como una pieza más. Eso sí, un contenedor no arranca en cero como un isolate: tarda minutos en aprovisionarse y solo está disponible en el plan de pago. Es la herramienta de último recurso, no la primera.
flowchart TB REQ[Peticion entrante] --> W[Worker recepcionista rapido] W -- responde ya --> USER[Usuario] W -- encola trabajo diferido --> Q[Queues consumidor en segundo plano] W -- inicia proceso de varios pasos --> WF[Workflows pasos durables con reintentos] W -- delega trabajo nativo o pesado --> C[Containers imagen en una VM linux] style W fill:#89b4fa,color:#11111b style Q fill:#a6e3a1,color:#11111b style WF fill:#cba6f7,color:#11111b style C fill:#fab387,color:#11111b
La lección que cierra este nivel, y que en el fondo cierra la idea entera de compute en el edge, es que los límites del Worker son un lenguaje de diseño disfrazado de restricciones. Un principiante los vive como fronteras que le impiden hacer cosas; un arquitecto los lee como un mapa que le dice dónde va cada cosa. El techo de CPU, la memoria compartida, las seis conexiones, el tamaño del bundle: cada número está calibrado para empujarte hacia una forma concreta de sistema, aquella en la que el Worker es una capa delgada, rápida y cercana al usuario que orquesta, y el trabajo pesado, lento o especial vive en piezas construidas para soportarlo. Esta es la inversión mental definitiva. En el modelo del servidor monolítico, una sola máquina hacía de todo —recibía la petición, calculaba, guardaba, esperaba— porque el recurso era indivisible y estaba reservado para ti; la arquitectura la dictaba la comodidad, no la física. En el edge, el recurso es compartido y afinado, y por eso la plataforma te ofrece un catálogo de piezas especializadas y te invita, con la gramática de sus límites, a repartir el trabajo entre ellas: Queues para lo que puede esperar, Workflows para lo que debe sobrevivir, Containers para lo que no cabe en un lenguaje seguro, Durable Objects para lo que necesita identidad y coordinación. El Worker deja de ser el obrero y se convierte en el director de orquesta que conoce a sus músicos. Cuando interiorizas esto, dejas de pelearte con los límites intentando meter más trabajo en una invocación, y empiezas a preguntarte lo único que importa: de todo lo que esta petición desencadena, ¿qué parte pertenece a la puerta rápida, y qué parte pertenece a otra pieza mejor preparada para cargarla? Esa pregunta, y no la potencia de una máquina, es el arte de diseñar en el edge.
- Toma un endpoint que hoy genera un informe pesado de forma síncrona y rediséñalo con
Queues: qué responde el productor y qué hace el consumidor. - Describe un proceso de tres pasos con esperas largas —pago, verificación, envío— y explica por qué un
Workflowconstep.do()lo modela mejor que tres colas encadenadas. - Enumera dos cargas que solo un
Containerpuede atender y razona por qué no caben en el modelo de isolates. - Explica por qué la clase
Containerextienda un Durable Object encaja con la idea de gestionar enrutado, estado y ciclo de vida. - Ante un cálculo intenso y único que roza el límite de CPU, argumenta cuándo basta con subir
cpu_msy cuándo conviene ya salir a otra pieza.