Apache Kafka se ha consolidado como el estándar de facto en el mundo de la mensajería distribuida y el procesamiento de flujos de datos. Desde su nacimiento en LinkedIn hasta su adopción global masiva, Kafka ha soportado múltiples casos de uso, como la agregación de logs, la mensajería en tiempo real y el procesamiento de eventos, gracias a su diseño orientado a sistemas distribuidos y alta escalabilidad. Sin embargo, Kafka fue ideado en una época en la que predominaban los centros de datos locales y no se anticipó la migración inminente hacia infraestructuras en la nube. Eso genera varios retos cuando se intenta ejecutar Kafka en entornos modernos basados en la nube, particularmente cuando se busca combinar su arquitectura con soluciones de almacenamiento de objetos como Amazon S3. Uno de los principales motivos para querer almacenar datos de Kafka en S3 es el ahorro significativo en costos frente al almacenamiento tradicional en disco.
La arquitectura original de Kafka combina el cómputo y el almacenamiento, lo que obliga a escalar ambos simultáneamente, generando a menudo un uso ineficiente de recursos. Además, Kafka depende de la replicación de datos para garantizar durabilidad y disponibilidad, lo que implica constantes transferencias entre máquinas e incluso entre zonas de disponibilidad (AZ) en la nube, elevando el costo debido a tarifas por transferencia cruzada entre zonas. Por ello, el almacenamiento de datos en S3, que ofrece mayor durabilidad y escalabilidad independiente por un costo reducido, se ha convertido en una tendencia creciente para las nuevas implementaciones compatibles con Kafka. El diseño de Kafka, basado en almacenamiento local en discos, presenta complicaciones importantes para su adaptación a objetos inmutables como los que ofrece S3. Las operaciones tradicionales en Kafka, como la escritura secuencial y la lectura eficiente de segmentos de logs, no encajan naturalmente con las características de la capa de almacenamiento en objetos, que tiene latencias considerablemente superiores y requiere la escritura de datos en bloques completos e inmutables en lugar de en archivos modificables.
Esto introduce un reto de latencia notable, dado que operaciones simples como acceder a un objeto en S3 suelen tardar decenas de milisegundos, en contraste con las microsegundos que puede tomar un SSD local. Para mitigar este problema, algunas soluciones sacrifican la latencia y reconocen la recepción de mensajes únicamente cuando estos se almacenan de forma segura en S3, aceptando un retraso prolongado para los productores. Otros proyectos, como AutoMQ, adoptan un enfoque híbrido que combina un Write Ahead Log (WAL) basado en almacenamiento de bloques rápido (por ejemplo, AWS EBS) con offloading asíncrono hacia S3. Esto permite mantener una baja latencia en las operaciones de escritura al devolver rápidamente la confirmación al productor una vez que los datos se escriben localmente, mientras que la replicación y durabilidad final se garantizan posteriormente en S3, logrando un balance adecuado entre costo y rendimiento. La cantidad de operaciones de escritura (IOPS) a S3 también representa un desafío importante.
Debido al modelo de costos basado en solicitudes PUT, realizar escrituras frecuentes y pequeñas puede disparar los gastos mensuales, especialmente en servicios que manejan miles de mensajes por segundo. Para contrarrestar esta limitación, las arquitecturas modernas emplean técnicas de batching, agrupando mensajes para escribir grandes bloques de datos que minimizan el número total de solicitudes. Sin embargo, esta estrategia afecta la latencia y la granularidad de las operaciones, además de complicar la lectura, ya que los datos de un solo partición pueden terminar distribuidos en múltiples objetos. Para asegurar un buen desempeño en la lectura, las soluciones deben implementar sistemas de compactación que consoliden y reorganicen los datos de manera eficiente. Esta compactación reduce la fragmentación de la información y facilita el acceso secuencial, lo que es fundamental para mantener una experiencia de lectura fluida y rentable en S3.
El manejo eficaz de la caché se vuelve clave para compensar las deficiencias de latencia y las limitaciones en IOPS del almacenamiento de objetos. Mantener datos calientes en memoria permite servir con rapidez las solicitudes de lectura y evita la sobrecarga de S3. Pero gestionar la caché presenta sus propias complejidades, principalmente relacionadas con la invalidez de la caché y la asignación de responsabilidades entre nodos en un clúster distribuido. Mantener la coherencia de la información en caché, particularmente para datos en constante cambio, requiere mecanismos sofisticados y eficientes. Las arquitecturas como AutoMQ buscan preservar la localidad de datos característica de Kafka, asegurando que cada broker sea responsable de los particiones asignados y pueda administrar su caché de forma localizada.
Al tener cachés diferenciadas para datos recientes (logs calientes) y para lecturas históricas (bloques fríos), se optimiza el flujo de datos y se reduce la presión sobre la capa de almacenamiento de objetos. Otro aspecto crítico es la gestión de metadatos. En Kafka, la estructura de datos se apoya en un sistema de ficheros local, lo que facilita, por ejemplo, la enumeración rápida de segmentos dentro de una partición. En S3, la operación equivalente, LIST, tiene un rendimiento limitado y costos asociados. Por ello, es necesario implementar mecanismos adicionales para manejar la metadata, mantener la información de segmentación, orden y ubicación de datos sin generar consultas excesivas a S3.
Las técnicas de compactación y consolidación de objetos contribuyen a minimizar la cantidad de metadatos y mantener un acceso ágil. En cuanto a la compatibilidad con Kafka, la transición a un backend basado en S3 implica reescribir la capa de almacenamiento sin alterar el protocolo ni la lógica interna de Kafka. Esto es un reto debido a las diferencias profundas en cómo se gestionan los archivos y la información. Algunas soluciones optan por rediseñar todo el protocolo para adaptarlo mejor al almacenamiento de objetos, lo cual puede ser más sencillo a nivel de desarrollo pero complica la compatibilidad total con Kafka y la futura integración con sus actualizaciones. Soluciones como AutoMQ utilizan un enfoque que preserva el protocolo íntegro de Kafka y reimplementan solo la capa de almacenamiento.
Este método abre la posibilidad de mantener una compatibilidad completa y aprovechar las mejoras futuras del ecosistema Kafka, pero requiere un trabajo técnico considerable para mapear las operaciones de almacenamiento de Kafka al modelo de objetos y garantizar el correcto funcionamiento de conceptos esenciales como segmentos, índices y transacciones. La estructura del almacenamiento en streams y segmentos es adaptada para gestionar la naturaleza inmutable de S3, combinando la persistencia en objetos con un manejo local temporal para lograr eficiencia. Esta convergencia entre la arquitectura tradicional compartida-nada (shared-nothing) de Kafka y el almacenamiento compartido (shared-disk) que ofrece S3 requiere equilibrar la localización de datos con la flexibilidad y la colaboración entre nodos. Uno de los puntos más complejos para operaciones en la nube es el costo adicional generado por el tráfico inter zona (cross-AZ). Kafka, al depender de líderes y seguidores repartidos entre zonas, incurre en gastos de transferencia elevadas tanto en la producción como en la replicación de mensajes.
Al usar un almacenamiento centralizado en S3, la replicación se simplifica y elimina este costo, pero el tráfico hacia los brokers líderes sigue existiendo. Para minimizar el tráfico cross-AZ, algunas soluciones realizan modificaciones en el proceso de descubrimiento de líderes, asignando brokers en la misma zona del productor que reciben inicialmente los mensajes y luego redistribuyen los datos internamente, lo que reduce notablemente los costos operativos. AutoMQ implementa un mecanismo donde los productores siempre se conectan con un broker en la misma zona, que actúa como buffer temporal y escribe a S3 antes de informarlo al broker líder que tiene la responsabilidad final. Finalmente, la gestión del throughput y el balance de carga en un entorno con almacenamiento en objetos también implica retos relevantes. Los brokers deben asumir responsabilidades adicionales como el buffering en memoria, la subida concurrente a S3, procesos de compactación y el manejo eficiente de la lectura y escritura, para no saturar la red ni el sistema.