La gestión de recursos efímeros y con estado, distribuidos globalmente, presenta desafíos inherentes de consistencia y latencia. En el contexto de los navegadores headless, que son recursos computacionales intensivos y con un ciclo de vida corto, la asignación eficiente y la actualización de su estado en tiempo real son críticas para la escalabilidad y la experiencia del usuario. Este artículo aborda cómo Cloudflare resolvió estos problemas al desacoplar Browser Run de una infraestructura compartida y adoptar un enfoque de contenedores dedicados, gestionando el estado con una base de datos transaccional distribuida y un sistema de colas para actualizaciones asíncronas.
El problema fundamental es cómo mantener una vista consistente y de baja latencia del estado de miles de recursos distribuidos globalmente, especialmente cuando la demanda es volátil y "spiky". La eventual consistencia de sistemas como Workers KV se convierte en un cuello de botella, introduciendo condiciones de carrera y sobre-asignación. La solución requiere un sistema que garantice la atomicidad en la asignación de recursos y permita actualizaciones de estado de alto throughput sin sacrificar la consistencia.
Arquitectura del Sistema
La arquitectura de Browser Run se basa en Cloudflare Workers para la lógica de enrutamiento y en Cloudflare Containers para ejecutar las instancias de navegadores headless. Inicialmente, Browser Run compartía infraestructura con Browser Isolation (BISO), lo que generaba problemas de latencia, distribución y contención de recursos debido a patrones de uso dispares. La migración clave fue hacia contenedores dedicados, habilitados por Durable Objects (DO)-enabled Containers, que permiten la creación de instancias de navegador cerca del punto de entrada de la solicitud.
Para mitigar la latencia entre Durable Objects y los contenedores que pueden estar geográficamente distantes, se implementaron 'regional pools' de contenedores pre-calentados. Esto asegura que un Durable Object y su contenedor asociado estén lo suficientemente cerca para mantener baja la latencia de comunicación. La gestión del estado de estos contenedores, incluyendo su disponibilidad y asignación, fue inicialmente manejada por Workers KV. Sin embargo, la eventual consistencia de KV (con TTLs de caché de hasta 30 segundos) resultó ser un cuello de botella crítico, causando condiciones de carrera y limitando la escalabilidad.
La solución a este problema de estado fue migrar de Workers KV a D1, la base de datos distribuida de Cloudflare basada en SQLite. D1 proporciona transacciones ACID, lo que permite una asignación atómica de navegadores a usuarios, eliminando las condiciones de carrera. Para manejar el alto volumen de actualizaciones de estado (cada contenedor actualiza su estado cada 5 segundos), se implementó un patrón de batching de escrituras utilizando Cloudflare Queues. Cada contenedor envía su estado a una cola regional, y un Worker consumidor procesa estos mensajes en lotes de hasta 100, con un timeout de 1 segundo, logrando un throughput de hasta 500,000 actualizaciones de contenedores por ubicación con una latencia P95 de 0.1ms para escrituras en lote. En caso de backlog en una cola, se implementa un fallback a una región de respaldo. Las 'Quick Actions' ahora se ejecutan enviando todos los parámetros en una única solicitud HTTP al contenedor, eliminando la comunicación WebSocket paso a paso y reduciendo significativamente la latencia.
Flujo de Asignación de Navegador
- 1 Usuario Inicia solicitud de navegador headless
- 2 Cloudflare Worker Recibe solicitud, determina región óptima
- 3 Durable Object Coordina la asignación dentro de la región
- 4 D1 (SQLite) Consulta pool de contenedores disponibles, realiza UPDATE transaccional para ...
- 5 Cloudflare Container Instancia de navegador headless asignada al usuario
Flujo de Actualización de Estado de Contenedor
- 1 Cloudflare Container Calcula su estado actual (cada 5 segundos)
- 2 Cloudflare Queue Añade mensaje de estado a la cola de su ubicación
- 3 Worker Consumer Procesa mensajes de la cola en lotes (max 100, timeout 1s)
- 4 D1 (SQLite) Actualiza el estado de los contenedores en lote
| Capa | Tecnología | Justificación |
|---|---|---|
| orchestration | Cloudflare Containers | Ejecución aislada y escalable de navegadores headless, con distribución global y pre-calentamiento. vs Infraestructura compartida con Browser Isolation (BISO) |
| compute | Cloudflare Workers | Lógica de enrutamiento de solicitudes, coordinación de Durable Objects, y consumidores de Queues para procesamiento de estado. |
| storage | Cloudflare D1 (SQLite) | Almacenamiento transaccional del estado de los contenedores, garantizando atomicidad en la asignación y consistencia. vs Cloudflare Workers KV Sharding por ubicación, batching de escrituras. |
| messaging | Cloudflare Queues | Buffer asíncrono para actualizaciones de estado de contenedores, permitiendo batching de escrituras a D1 y desacoplando productores de consumidores. max_batch_size: 100, max_batch_timeout: 1s |
| orchestration | Cloudflare Durable Objects | Coordinación de estado y persistencia de sesión para instancias de navegador, garantizando que un navegador esté cerca de la solicitud entrante. |
Trade-offs
Ganancias
- ▲▲ Capacidad de navegadores concurrentes
- ▲ Latencia de Quick Actions
- ▲ Flexibilidad de actualización de imagen de navegador
- ▲ Consistencia del estado de asignación
- ▲▲ Throughput de actualizaciones de estado
Costes
- △ Complejidad arquitectónica (regional pools, D1, Queues)
- △ Fricción inicial con plataforma Containers en beta
WITH candidate_pool AS (
-- candidate pool logic to pick based on latency and other rules
)
UPDATE containers
SET status = 'picked'
WHERE sessionId IN (
SELECT sessionId
FROM candidate_pool
ORDER BY RANDOM()
LIMIT ?5
)
RETURNING data{
...
"queues": {
"consumers": [
{
"queue": "production-core-containers-queue-weur",
"max_batch_size": 100,
"max_batch_timeout": 1,
"max_retries": 1
}
]
}
}Fundamentos Teóricos
El desafío de mantener la consistencia del estado en sistemas distribuidos es un tema central en la informática distribuida, abordado por el Teorema CAP (Consistency, Availability, Partition Tolerance). La elección de D1 sobre Workers KV para la gestión del estado de los contenedores ilustra una priorización de la Consistencia (C) y la Disponibilidad (A) sobre la eventual consistencia, especialmente en un contexto donde las condiciones de carrera son inaceptables. D1, al ser una base de datos transaccional, se alinea con los principios de sistemas que requieren fuertes garantías de consistencia, similares a los discutidos en trabajos sobre bases de datos distribuidas y protocolos de consenso como Paxos o Raft, aunque D1 utiliza SQLite con un enfoque de replicación y consistencia diferente.
La implementación de 'regional pools' para reducir la latencia entre componentes distribuidos es una aplicación práctica de los principios de localidad de datos y minimización de la latencia de red, un problema fundamental en sistemas distribuidos a gran escala. La técnica de batching de escrituras para mejorar el throughput en D1 es una optimización común en sistemas de bases de datos y mensajería, donde se busca amortizar el costo fijo de las operaciones de I/O y de red, un concepto bien conocido en la optimización de sistemas de almacenamiento y procesamiento de eventos.