El problema fundamental que K2 aborda es la desalineación de escala y tiempo entre productores y consumidores en arquitecturas distribuidas, particularmente en entornos de edge computing. Los sistemas RPC tradicionales exigen que productores y consumidores operen a ritmos similares, lo que lleva a la pérdida de eventos cuando la demanda excede la capacidad de procesamiento o cuando los servicios están temporalmente no disponibles. Esto se agrava en escenarios con múltiples consumidores que requieren procesar los mismos datos de forma independiente.
K2 resuelve esto introduciendo una capa intermedia de almacenamiento duradero que absorbe las escrituras de los productores y permite a los consumidores leer los datos a su propio ritmo. La elección de construir este sistema sobre almacenamiento de objetos (R2) es una decisión arquitectónica clave, impulsada por las limitaciones y ventajas de la infraestructura global de Cloudflare: máquinas efímeras y distribuidas geográficamente, pero con una capacidad de escalado horizontal masiva y proximidad al usuario. Esta aproximación contrasta con las implementaciones tradicionales de sistemas de streaming como Apache Kafka, que a menudo requieren clústeres dedicados y persistentes.
Arquitectura del Sistema
K2 implementa un log duradero y particionado sobre Cloudflare R2, un sistema de almacenamiento de objetos con alta durabilidad (11 nueves) y APIs fuertemente consistentes. La capa de aplicación de K2 se simplifica al delegar la replicación y el consenso al almacenamiento de objetos subyacente. Esta separación de cómputo y almacenamiento permite escalar cada componente de forma independiente y almacenar grandes volúmenes de datos históricos a bajo costo.
Dado que R2 no soporta operaciones de append directamente, K2 acumula escrituras en memoria en un servicio de edge. Después de un corto período, estos eventos se escriben como archivos de segmento completos en R2. El ordenamiento y los offsets estrictamente incrementales se logran utilizando las operaciones atómicas de R2, eliminando la necesidad de un servicio de coordinación separado. Los consumidores pueden leer los datos de varias maneras, incluyendo la división de lecturas entre un conjunto de consumidores para paralelismo o el patrón pub/sub donde todos los mensajes se entregan a todos los consumidores. El sistema maneja el consumo mediante un modelo de lease, donde los clientes 'consumen' un batch de eventos, obtienen un lease por un período determinado, y luego deben 'ack', 'nack' o 'extender' el lease. Esto asegura la entrega 'at-least-once' y permite la reintentos en caso de fallos de procesamiento.
Flujo de Producción de Eventos K2
- 1 Productor Envía eventos a un servicio de edge de K2
- 2 Servicio Edge K2 Acumula eventos en memoria
- 3 Servicio Edge K2 Espera un período corto para acumular un batch
- 4 Servicio Edge K2 Escribe el batch como un archivo de segmento completo en R2
- 5 Cloudflare R2 Almacena el segmento de forma duradera y consistente
Flujo de Consumo de Eventos K2
- 1 Consumidor Solicita un batch de eventos de una suscripción K2
- 2 Servicio K2 Asigna un lease al consumidor para un batch de eventos
- 3 Consumidor Procesa los eventos del batch
- 4 Consumidor Envía 'ack' si el procesamiento fue exitoso
- 5 Servicio K2 Marca el batch como procesado, no se redeliverá
| Capa | Tecnología | Justificación |
|---|---|---|
| storage | Cloudflare R2 | Almacenamiento de objetos duradero y consistente para los segmentos del log de eventos. Proporciona 11 nueves de durabilidad y APIs atómicas para garantizar el ordenamiento y los offsets. vs Apache Kafka (no apto para la infraestructura de edge de Cloudflare), Bases de datos distribuidas tradicionales (complejidad operativa en el edge) |
| compute | Cloudflare Workers | Plataforma serverless para la lógica de negocio de K2, incluyendo la acumulación de eventos en memoria y la escritura de segmentos a R2. También se utiliza para la interacción de los usuarios con la API de K2. |
| networking | HTTP API | Interfaz principal para la producción y consumo de eventos, permitiendo la integración con cualquier cliente HTTP o Cloudflare Workers. |
Trade-offs
Ganancias
- ▲ Durabilidad de datos
- ▲▲ Escalabilidad de almacenamiento
- ▲ Simplicidad de la capa de aplicación
- ▲ Costo de almacenamiento a largo plazo
Costes
- ▲ Latencia de producción (produce latency)
- △ Retries a nivel de mensaje
const result = await env.EVENTS.send([
{
content: new TextEncoder().encode(
JSON.stringify({
event: "page_view",
path: new URL(request.url).pathname,
timestamp: Date.now(),
}),
),
headers: { "content-type": "application/json" },
},
]);
if (!result.success) {
console.error(`Produce failed: ${result.error.message}`);
return new Response("Failed to record event", {
status: result.error.retryable ? 503 : 500,
});
}curl -X POST "https://4d8f5394e3e733debdeca9c65c5b7439.k2.cloudflarestorage.com/subscriptions/ee13f761783d3823a447a47b572ebf76/consume" \
-H "Authorization: Bearer ${CLOUDFLARE_API_TOKEN}" \
-H "Content-Type: application/json" \
--data '{
"worker_id": "analytics-1",
"max_records": 100
}'curl -X POST "https://d78b09ee1f50430e9ec92a8af92b0231.k2.cloudflarestorage.com/subscriptions/ee13f761783d3823a447a47b572ebf76/batches/b7e4c9210a3f468d95c2e1068fdb734a/ack" \
-H "Authorization: Bearer ${CLOUDFLARE_API_TOKEN}" \
-H "Content-Type: application/json" \
--data '{ "worker_id": "analytics-1" }'Fundamentos Teóricos
El concepto de un log de eventos inmutable y duradero como primitivo central en sistemas distribuidos tiene sus raíces en trabajos académicos sobre bases de datos y sistemas de archivos distribuidos. La idea de un 'log-structured file system' (LFS), introducida por Mendel Rosenblum y John Ousterhout en 1991, propuso que escribir todos los cambios en un log secuencial puede mejorar el rendimiento de escritura, especialmente en discos con latencias de búsqueda elevadas. Aunque K2 no es un sistema de archivos, la estrategia de acumular escrituras y luego persistirlas en bloques más grandes en un almacenamiento de objetos que no soporta appends directamente, refleja principios similares de optimización de escrituras secuenciales.
La delegación de la durabilidad y consistencia a la capa de almacenamiento subyacente (R2) también se alinea con el principio de 'shared-nothing architecture' y la evolución hacia arquitecturas de 'disaggregated storage', donde el cómputo y el almacenamiento se gestionan de forma independiente. Esto permite que la capa de aplicación se centre en la lógica de negocio, mientras que la capa de almacenamiento maneja las complejidades de la replicación, la tolerancia a fallos y la consistencia, un patrón que ha sido explorado extensamente en la investigación de bases de datos distribuidas y sistemas de archivos en la nube.