Saltearse al contenido

Logs - Lo que todo ingeniero de software debería saber sobre la abstracción unificadora de los datos en tiempo real

Me uní a LinkedIn hace aproximadamente seis años, en un momento particularmente interesante. Justo comenzábamos a toparnos con los límites de nuestra base de datos monolítica y centralizada y necesitábamos empezar la transición hacia un portafolio de sistemas distribuidos especializados. Esta ha sido una experiencia interesante: construimos, desplegamos y, hasta el día de hoy, operamos una base de datos de grafos distribuida, un backend de búsqueda distribuida, una instalación de Hadoop y una tienda de clave-valor de primera y segunda generación.

Una de las cosas más útiles que aprendí en todo esto fue que muchas de las cosas que estábamos construyendo tenían un concepto muy simple en su corazón: el log. A veces llamados write-ahead logs o commit logs o transaction logs, los logs han existido casi tanto como las computadoras y están en el corazón de muchos sistemas de datos distribuidos y arquitecturas de aplicaciones en tiempo real.

No puedes comprender completamente las bases de datos, NoSQL stores,key value stores, replicación, Paxos, Hadoop, control de versiones o casi cualquier sistema de software sin entender los logs; y sin embargo, la mayoría de los ingenieros de software no están familiarizados con ellos. Me gustaría cambiar eso. En este post, te guiaré a través de todo lo que necesitas saber sobre los logs, incluyendo qué es un log y cómo usar logs para la integración de datos, procesamiento en tiempo real y construcción de sistemas.

Parte Uno: ¿Qué es un Log?

Un log es quizás la abstracción de almacenamiento más simple posible. Es una secuencia totalmente ordenada de registros, a la que solo se añaden elementos, ordenada por tiempo. Se ve así:

Logs

Los registros son añadidos al final del log, y las lecturas proceden de izquierda a derecha. A cada entrada se le asigna un número único secuencial de entrada de log.

El ordenamiento de los registros define una noción de “tiempo”, ya que las entradas a la izquierda se definen como más antiguas que las entradas a la derecha. El número de entrada del log puede ser considerado como el “timestamp” de la entrada. Describir este ordenamiento como una noción de tiempo parece un poco extraño al principio, pero tiene la conveniente propiedad de que está desacoplado de cualquier reloj físico particular. Esta propiedad resultará ser esencial cuando lleguemos a sistemas distribuidos.

El contenido y formato de los registros no son importantes para los propósitos de esta discusión. Además, no podemos simplemente seguir añadiendo registros al log ya que eventualmente nos quedaremos sin espacio. Volveré a esto en un momento.

Así que, un log no es tan diferente de un archivo o una tabla. Un archivo es un array de bytes, una tabla es un array de registros, y un log es realmente solo una especie de tabla o archivo donde los registros están ordenados por tiempo.

En este punto podrías estar preguntándote por qué vale la pena hablar de algo tan simple. ¿Cómo está relacionada de alguna manera una secuencia de registros a la que solo se añaden elementos con los sistemas de datos? La respuesta es que los logs tienen un propósito específico: registran qué ocurrió y cuándo. Para sistemas de datos distribuidos esto es, en muchos sentidos, el mismo corazón del problema.

Pero antes de que vayamos muy lejos, permíteme aclarar algo que es un poco confuso. Cada programador está familiarizado con otra definición de logging — los mensajes de error no estructurados o información de rastreo que una aplicación podría escribir en un archivo local usando syslog o log4j. Para claridad, llamaré a esto “application logging”. El application log es una forma degenerativa del concepto de log que estoy describiendo. La mayor diferencia es que los text logs están destinados principalmente para que los humanos los lean y los “journal” o “data logs” que estoy describiendo están construidos para acceso programático.

(En realidad, si lo piensas, la idea de que los humanos lean a través de logs en máquinas individuales es algo así como un anacronismo. Este enfoque rápidamente se convierte en una estrategia inmanejable cuando se involucran muchos servicios y servidores y el propósito de los logs rápidamente se convierte en una entrada para consultas y gráficos para entender el comportamiento a través de muchas máquinas — algo para lo cual el texto en inglés en archivos no es casi tan apropiado como el log estructurado descrito aquí.)

Logs en bases de datos

No sé de dónde se originó el concepto de log — probablemente es una de esas cosas como la búsqueda binaria que es demasiado simple para que el inventor se dé cuenta de que fue una invención. Está presente desde los primeros días de System R de IBM. El uso en bases de datos tiene que ver con mantener sincronizadas las diversas estructuras de datos e índices en presencia de fallos. Para hacer esto atómico y duradero, una base de datos usa un log para escribir información sobre los registros que modificarán, antes de aplicar los cambios a todas las diversas estructuras de datos que mantiene. El log es el registro de lo que ocurrió, y cada tabla o índice es una proyección de esta historia en alguna estructura de datos o índice útil. Dado que el log es persistido inmediatamente, se utiliza como la fuente autorizada para restaurar todas las demás estructuras persistentes en caso de un fallo.

Con el tiempo, el uso del log creció de ser un detalle de implementación de ACID a un método para replicar datos entre bases de datos. Resulta que la secuencia de cambios que ocurrieron en la base de datos es exactamente lo que se necesita para mantener una réplica remota de la base de datos sincronizada. Oracle, MySQL y PostgreSQL incluyen protocolos de envío de logs para transmitir porciones de log a bases de datos réplica que actúan como esclavas. Oracle ha comercializado el log como un mecanismo general de suscripción de datos para suscriptores de datos no Oracle con sus XStreams y GoldenGate e instalaciones similares en MySQL y PostgreSQL son componentes clave de muchas arquitecturas de datos.

Debido a este origen, el concepto de un log legible por máquina ha estado en gran medida confinado a los internos de las bases de datos. El uso de logs como mecanismo para la suscripción de datos parece haber surgido casi por casualidad. Pero esta misma abstracción es ideal para soportar todo tipo de mensajería, flujo de datos y procesamiento de datos en tiempo real.

Logs en sistemas distribuidos

Los dos problemas que un log resuelve —ordenar cambios y distribuir datos— son incluso más importantes en sistemas de datos distribuidos. Acordar un ordenamiento para las actualizaciones (o acordar discrepar y lidiar con los efectos secundarios) están entre los problemas centrales de diseño para estos sistemas.

El enfoque centrado en logs para sistemas distribuidos surge de una observación simple que llamaré el Principio de Replicación de Máquinas de Estado:

Si dos procesos determinísticos idénticos comienzan en el mismo estado y reciben las mismas entradas en el mismo orden, producirán la misma salida y terminarán en el mismo estado.

Esto puede parecer un poco obtuso, así que profundicemos y comprendamos lo que significa.

Determinístico significa que el procesamiento no depende del tiempo y no permite que ninguna otra entrada “fuera de banda” influya en sus resultados. Por ejemplo, un programa cuya salida es influenciada por el orden particular de ejecución de hilos o por una llamada a gettimeofday o alguna otra cosa no repetible generalmente se considera no determinístico.

El estado del proceso es cualquier dato que permanezca en la máquina, ya sea en memoria o en disco, al final del procesamiento.

La parte sobre recibir la misma entrada en el mismo orden debería sonar familiar —ahí es donde entra el log. Esta es una noción muy intuitiva: si alimentas dos piezas determinísticas de código con el mismo log de entrada, producirán la misma salida.

La aplicación a la informática distribuida es bastante obvia. Puedes reducir el problema de hacer que múltiples máquinas hagan lo mismo al problema de implementar un log distribuido consistente para alimentar estas entradas de procesos. El propósito del log aquí es exprimir toda la no determinística fuera de la corriente de entrada para asegurar que cada réplica procesando esta entrada permanezca sincronizada.

Cuando lo entiendes, no hay nada complicado o profundo acerca de este principio: más o menos equivale a decir “el procesamiento determinístico es determinístico”. Sin embargo, creo que es una de las herramientas más generales para el diseño de sistemas distribuidos.

Una de las cosas bellas de este enfoque es que los timestamps que indexan el log ahora actúan como el reloj para el estado de las réplicas —puedes describir cada réplica con un solo número, el timestamp para la entrada de log máxima que ha procesado. Este timestamp combinado con el log captura de manera única todo el estado de la réplica.

Registros en sistemas distribuidos

Hay una multitud de formas de aplicar este principio en sistemas, dependiendo de lo que se coloque en el registro. Por ejemplo, podemos registrar las solicitudes entrantes a un servicio, o los cambios de estado que el servicio experimenta en respuesta a una solicitud, o los comandos de transformación que ejecuta. Teóricamente, incluso podríamos registrar una serie de instrucciones de máquina para que cada réplica las ejecute o el nombre del método y los argumentos para invocar en cada réplica. Siempre que dos procesos procesen estas entradas de la misma manera, los procesos permanecerán consistentes a través de las réplicas.

Diferentes grupos de personas parecen describir los usos de los registros de manera diferente. La gente de bases de datos generalmente diferencia entre el registro físico y lógico. El registro físico significa registrar el contenido de cada fila que se cambia. El registro lógico significa no registrar las filas cambiadas, sino los comandos SQL que llevan a los cambios de fila (las declaraciones de insertar, actualizar y eliminar).

La literatura de sistemas distribuidos comúnmente distingue dos enfoques generales para el procesamiento y la replicación. El “modelo de máquina de estado” generalmente se refiere a un modelo activo-activo donde mantenemos un registro de las solicitudes entrantes y cada réplica procesa cada solicitud. Una ligera modificación de esto, llamada “modelo de respaldo primario”, consiste en elegir una réplica como líder y permitir que este líder procese las solicitudes en el orden en que llegan y registre los cambios en su estado a partir del procesamiento de las solicitudes. Las otras réplicas aplican en orden los cambios de estado que hace el líder para que estén sincronizadas y listas para tomar el control como líder en caso de que el líder falle.

Logs

Para entender la diferencia entre estos dos enfoques, consideremos un problema de juguete. Imagina un “servicio aritmético” replicado que mantiene un solo número como su estado (inicializado a cero) y aplica adiciones y multiplicaciones a este valor. El enfoque activo-activo podría registrar las transformaciones a aplicar, digamos “+1”, “*2”, etc. Cada réplica aplicaría estas transformaciones y, por lo tanto, pasaría por el mismo conjunto de valores. El enfoque “activo-pasivo” tendría un único maestro que ejecutaría las transformaciones y registraría el resultado, digamos “1”, “3”, “6”, etc. Este ejemplo también aclara por qué el orden es clave para garantizar la consistencia entre réplicas: reordenar una adición y una multiplicación dará un resultado diferente.

Logs

El log distribuido puede ser visto como la estructura de datos que modela el problema del consenso. Después de todo, un log representa una serie de decisiones sobre el “siguiente” valor a añadir. Hay que entrecerrar un poco los ojos para ver un log en la familia de algoritmos Paxos, aunque la construcción de logs es su aplicación práctica más común. Con Paxos, esto suele hacerse utilizando una extensión del protocolo llamada “multi-paxos”, que modela el log como una serie de problemas de consenso, uno para cada slot en el log. El log es mucho más prominente en otros protocolos como ZAB, RAFT, y Viewstamped Replication, que modelan directamente el problema de mantener un log distribuido y consistente.

Mi sospecha es que nuestra visión de esto está un poco sesgada por el camino de la historia, quizás debido a las pocas décadas en las que la teoría de la computación distribuida superó su aplicación práctica. En realidad, el problema del consenso es un poco demasiado simple. Los sistemas informáticos rara vez necesitan decidir un único valor, casi siempre manejan una secuencia de solicitudes. Por lo tanto, un log, en lugar de un simple registro de un solo valor, es la abstracción más natural.

Además, el enfoque en los algoritmos oscurece la subyacente abstracción de log que los sistemas necesitan. Sospecho que terminaremos enfocándonos más en el log como un bloque de construcción comoditizado independientemente de su implementación de la misma manera que a menudo hablamos de una tabla hash sin molestarnos en entrar en los detalles de si nos referimos al hash murmur con sondeo lineal o alguna otra variante. El log se convertirá en algo así como una interfaz comoditizada, con muchos algoritmos e implementaciones compitiendo para proporcionar las mejores garantías y un rendimiento óptimo.

Changelog 101: Tablas y Eventos son Duales

Volviendo un poco a las bases de datos. Hay una dualidad fascinante entre un log de cambios y una tabla. El log es similar a la lista de todos los créditos y débitos y procesos bancarios; una tabla es todos los saldos actuales de las cuentas. Si tienes un log de cambios, puedes aplicar estos cambios en orden para crear la tabla que captura el estado actual. Esta tabla registrará el estado más reciente para cada clave (hasta un tiempo de log particular). Hay un sentido en el cual el log es la estructura de datos más fundamental: además de crear la tabla original, también puedes transformarlo para crear todo tipo de tablas derivadas. (Y sí, tabla puede significar almacenamiento de datos con clave para los no relacionales).

Logs

Este proceso también funciona al revés: si tienes una tabla que recibe actualizaciones, puedes registrar estos cambios y publicar un “changelog” de todas las actualizaciones al estado de la tabla. Este changelog es exactamente lo que necesitas para soportar réplicas en tiempo casi real. Así que en este sentido puedes ver las tablas y los eventos como duales: las tablas soportan datos en reposo y los logs capturan el cambio. La magia del log es que si es un log completo de cambios, no solo contiene los contenidos de la versión final de la tabla, sino que también permite recrear todas las demás versiones que podrían haber existido. Es, efectivamente, una especie de respaldo de cada estado previo de la tabla.

Esto podría recordarte al control de versiones del código fuente. Hay una relación cercana entre el control de versiones y las bases de datos. El control de versiones resuelve un problema muy similar al que tienen que resolver los sistemas de datos distribuidos: gestionar cambios distribuidos y concurrentes en el estado. Un sistema de control de versiones suele modelar la secuencia de parches, que es efectivamente un log. Interactúas directamente con un “snapshot” actualizado del código actual, que es análogo a la tabla. Notarás que en los sistemas de control de versiones, como en otros sistemas distribuidos con estado, la replicación ocurre a través del log: cuando actualizas, solo descargas los parches y los aplicas a tu snapshot actual.

Algunas personas han visto algunas de estas ideas recientemente desde Datomic, una empresa que vende una base de datos centrada en logs. Esta presentación ofrece una gran visión general de cómo han aplicado la idea en su sistema. Estas ideas no son únicas de este sistema, por supuesto, ya que han sido parte de la literatura de sistemas distribuidos y bases de datos durante más de una década.

Todo esto puede parecer un poco teórico. ¡No desesperes! Llegaremos a cosas prácticas bastante rápido.

Lo que sigue

En el resto de este artículo intentaré dar una idea de para qué sirve un log que va más allá de los aspectos internos de la computación distribuida o los modelos abstractos de computación distribuida. Esto incluye:

  1. Integración de Datos—Hacer que todos los datos de una organización estén fácilmente disponibles en todos sus sistemas de almacenamiento y procesamiento.
  2. Procesamiento de datos en tiempo real—Calcular flujos de datos derivados.
  3. Diseño de sistema distribuido—Cómo los sistemas prácticos pueden simplificarse con un diseño centrado en logs.

Estos usos giran todos en torno a la idea de un log como un servicio independiente.

En cada caso, la utilidad del log proviene de la simple función que proporciona el log: producir un registro persistente y rejugable de la historia. Sorprendentemente, en el núcleo de estos problemas está la capacidad de tener muchas máquinas que reproduzcan la historia a su propio ritmo de manera determinista.

Parte Dos: Integración de Datos

Permíteme primero decir lo que quiero decir con “integración de datos” y por qué creo que es importante, luego veremos cómo se relaciona con los logs.

La integración de datos implica hacer que todos los datos que una organización tiene estén disponibles en todos sus servicios y sistemas.

Esta frase “integración de datos” no es tan común, pero no conozco una mejor. El término más reconocible ETL generalmente solo cubre una parte limitada de la integración de datos—poblar un almacén de datos relacional. Pero mucho de lo que estoy describiendo puede pensarse como ETL generalizado para cubrir sistemas en tiempo real y flujos de procesamiento.

Logs

No se escucha mucho acerca de la integración de datos en todo el interés y el bombo alrededor de la idea de big data, pero no obstante, creo que este problema mundano de “hacer que los datos estén disponibles” es una de las cosas más valiosas en las que una organización puede enfocarse.

El uso efectivo de los datos sigue una especie de jerarquía de necesidades de Maslow. La base de la pirámide implica capturar todos los datos relevantes, ser capaz de reunirlos en un entorno de procesamiento aplicable (ya sea un sistema de consultas en tiempo real sofisticado o simplemente archivos de texto y scripts en python). Estos datos necesitan ser modelados de una manera uniforme para facilitar su lectura y procesamiento. Una vez que estas necesidades básicas de capturar datos de una manera uniforme están cubiertas, es razonable trabajar en infraestructuras para procesar estos datos de diversas maneras: MapReduce, sistemas de consultas en tiempo real, etc.

Vale la pena señalar lo obvio: sin un flujo de datos confiable y completo, un clúster de Hadoop no es más que un calentador de espacio muy caro y difícil de montar. Una vez que los datos y el procesamiento están disponibles, se puede trasladar la preocupación a problemas más refinados de buenos modelos de datos y semánticas bien entendidas y consistentes. Finalmente, la concentración puede desplazarse hacia procesamientos más sofisticados: mejor visualización, informes, y procesamiento y predicción algorítmica.

En mi experiencia, la mayoría de las organizaciones tienen enormes vacíos en la base de esta pirámide: carecen de un flujo de datos completo y confiable, pero quieren saltar directamente a técnicas avanzadas de modelado de datos. Esto es completamente al revés.

Entonces, la pregunta es, ¿cómo podemos construir un flujo de datos confiable a través de todos los sistemas de datos en una organización?

Integración de Datos: Dos complicaciones

Dos tendencias hacen que la integración de datos sea más difícil.

La manguera de datos de eventos

La primera tendencia es el auge de los datos de eventos. Los datos de eventos registran cosas que suceden en lugar de cosas que son. En los sistemas web, esto significa el registro de la actividad del usuario, pero también los eventos y estadísticas a nivel de máquina requeridos para operar y monitorear de manera confiable el valor de un centro de datos en máquinas. La gente tiende a llamar a esto “datos de log” ya que a menudo se escribe en logs de aplicaciones, pero eso confunde la forma con la función. Estos datos están en el corazón de la web moderna: la fortuna de Google, después de todo, es generada por un canal de relevancia construido sobre clics e impresiones, es decir, eventos.

Y estas cosas no están limitadas a las compañías web, es solo que las compañías web ya están completamente digitalizadas, por lo que son más fáciles de instrumentar. Los datos financieros han sido centrados en eventos desde hace mucho tiempo. RFID añade este tipo de seguimiento a objetos físicos. Creo que esta tendencia continuará con la digitalización de negocios y actividades tradicionales.

Este tipo de datos de eventos registra lo que sucedió y tiende a ser varias órdenes de magnitud mayor que los usos tradicionales de bases de datos. Esto presenta desafíos significativos para el procesamiento.

La explosión de sistemas de datos especializados

La segunda tendencia proviene de la explosión de sistemas de datos especializados que se han vuelto populares y a menudo están disponibles gratuitamente en los últimos cinco años. Existen sistemas especializados para OLAP, búsqueda, almacenamiento en línea simple, procesamiento por lotes, análisis de gráficos, y así sucesivamente.

La combinación de más datos de más variedades y un deseo de llevar estos datos a más sistemas conduce a un enorme problema de integración de datos.

Flujo de datos estructurado en log

El log es la estructura de datos natural para manejar el flujo de datos entre sistemas. La receta es muy simple:

Toma todos los datos de la organización y colócalos en un log central para suscripción en tiempo real.

Cada fuente de datos lógica puede ser modelada como su propio log. Una fuente de datos podría ser una aplicación que registra eventos (digamos clics o vistas de página), o una tabla de base de datos que acepta modificaciones. Cada sistema suscriptor lee de este log tan rápido como puede, aplica cada nuevo registro a su propia tienda y avanza su posición en el log. Los suscriptores podrían ser cualquier tipo de sistema de datos: una caché, Hadoop, otra base de datos en otro sitio, un sistema de búsqueda, etc.

Logs

Por ejemplo, el concepto de log proporciona un reloj lógico para cada cambio respecto al cual todos los suscriptores pueden ser medidos. Esto facilita mucho razonar sobre el estado de los diferentes sistemas suscriptores respecto a los demás, ya que cada uno tiene un “punto en el tiempo” hasta el que han leído.

Para hacer esto más concreto, considere un caso simple donde hay una base de datos y una colección de servidores de caché. El log proporciona una manera de sincronizar las actualizaciones a todos estos sistemas y razonar sobre el punto en el tiempo de cada uno de estos sistemas. Digamos que escribimos un registro con la entrada de log X y luego necesitamos hacer una lectura desde la caché. Si queremos garantizar que no vemos datos obsoletos, solo necesitamos asegurarnos de no leer de ninguna caché que no se haya replicado hasta X.

El log también actúa como un buffer que hace que la producción de datos sea asíncrona con respecto al consumo de datos. Esto es importante por muchas razones, pero especialmente cuando hay múltiples suscriptores que pueden consumir a diferentes velocidades. Esto significa que un sistema suscriptor puede fallar o bajar para mantenimiento y ponerse al día cuando vuelva: el suscriptor consume a un ritmo que él controla. Un sistema de lotes como Hadoop o un almacén de datos puede consumir solo cada hora o cada día, mientras que un sistema de consultas en tiempo real puede necesitar estar al segundo. Ni la fuente de datos original ni el log tienen conocimiento de los diversos sistemas de destino de datos, por lo que los sistemas consumidores pueden ser añadidos y eliminados sin ningún cambio en el pipeline.

Logs

“Todos los flujos de datos funcionales están diseñados como un log; cada flujo de datos roto, está roto a su manera”. —Conde Leo Tolstoy (traducción del autor)

De particular importancia: el sistema de destino solo sabe sobre el log y no sobre ningún detalle del sistema de origen. El sistema consumidor no necesita preocuparse de si los datos provinieron de un RDBMS, de una moderna tienda de clave-valor, o fueron generados sin un sistema de consulta en tiempo real de ningún tipo. Esto puede parecer un punto menor, pero en realidad es crítico.

Utilizo el término “log” aquí en lugar de “sistema de mensajes” o “pub sub” porque es mucho más específico acerca de la semántica y una descripción mucho más cercana de lo que necesitas en una implementación práctica para respaldar la replicación de datos. He descubierto que “publicar suscribir” no implica mucho más que direccionamiento indirecto de mensajes —si comparas cualquier sistema de mensajería que prometa publicar-suscribir, descubres que garantizan cosas muy diferentes, y la mayoría de los modelos no son útiles en este dominio. Puedes pensar en el log como un tipo de sistema de mensajería con garantías de durabilidad y semánticas de ordenación fuertes. En sistemas distribuidos, este modelo de comunicación a veces va por el (algo terrible) nombre de difusión atómica.

Vale la pena enfatizar que el log sigue siendo solo la infraestructura. Eso no es el final de la historia de dominar el flujo de datos: el resto de la historia gira en torno a metadatos, esquemas, compatibilidad y todos los detalles del manejo de la estructura y evolución de datos. Pero hasta que haya una manera confiable y general de manejar la mecánica del flujo de datos, los detalles semánticos son secundarios.

En LinkedIn

Tuve la oportunidad de observar este problema de integración de datos emerger en avance rápido a medida que LinkedIn pasaba de una base de datos relacional centralizada a una colección de sistemas distribuidos.

Hoy en día, nuestros principales sistemas de datos incluyen:

Cada uno de estos es un sistema distribuido especializado que proporciona funcionalidades avanzadas en su área de especialidad.

Esta idea de usar logs para el flujo de datos ha estado flotando en LinkedIn desde incluso antes de que yo llegara. Una de las primeras piezas de infraestructura que desarrollamos fue un servicio llamado databus que proporcionaba una abstracción de caché de log sobre nuestras primeras tablas Oracle para escalar la suscripción a cambios en la base de datos para que pudiéramos alimentar nuestro grafo social y los índices de búsqueda.

Daré un poco de la historia para proporcionar contexto. Mi propia involucración en esto comenzó alrededor de 2008 después de que hubiéramos lanzado nuestra tienda de clave-valor. Mi siguiente proyecto fue tratar de poner en marcha una configuración de Hadoop en funcionamiento y mover algunos de nuestros procesos de recomendación allí. Teniendo poca experiencia en esta área, naturalmente presupuestamos unas pocas semanas para obtener datos de entrada y salida, y el resto de nuestro tiempo para implementar algoritmos de predicción sofisticados. Así comenzó una larga travesía.

Inicialmente planeamos simplemente raspar los datos de nuestro actual almacén de datos Oracle. El primer descubrimiento fue que sacar datos de Oracle rápidamente es una especie de arte oscuro. Peor aún, el procesamiento del almacén de datos no era apropiado para el procesamiento de lotes de producción que planeábamos para Hadoop: gran parte del procesamiento no era reversible y específico para los informes que se estaban haciendo. Terminamos evitando el almacén de datos y yendo directamente a las bases de datos de origen y los archivos de log. Finalmente, implementamos otro pipeline para cargar datos en nuestra tienda de clave-valor para servir resultados.

Esta copia de datos mundana terminó siendo uno de los elementos dominantes para el desarrollo original. Peor aún, cada vez que había un problema en cualquiera de los pipelines, el sistema Hadoop era en gran medida inútil: ejecutar algoritmos sofisticados en datos malos simplemente produce más datos malos.

Aunque habíamos construido cosas de una manera bastante genérica, cada nueva fuente de datos requería una configuración personalizada para configurar. También resultó ser la fuente de un gran número de errores y fallos. Las características del sitio que habíamos implementado en Hadoop se volvieron populares y nos encontramos con una larga lista de ingenieros interesados. Cada usuario tenía una lista de sistemas con los que querían integrarse y una larga lista de nuevos feeds de datos que querían.

Logs

“ETL en la Antigua Grecia. No ha cambiado mucho.

Algunas cosas se volvieron lentamente claras para mí.

Primero, los pipelines que habíamos construido, aunque un poco desordenados, eran en realidad extremadamente valiosos. Solo el proceso de hacer que los datos estuvieran disponibles en un nuevo sistema de procesamiento (Hadoop) desbloqueó muchas posibilidades. Era posible realizar nuevos cálculos en los datos que habrían sido difíciles de hacer antes. Muchos productos y análisis nuevos surgieron simplemente al juntar múltiples piezas de datos que previamente estaban encerradas en sistemas especializados.

Segundo, estaba claro que las cargas de datos confiables requerirían un profundo soporte del pipeline de datos. Si capturábamos toda la estructura que necesitábamos, podríamos hacer que las cargas de datos de Hadoop fueran totalmente automáticas, de modo que no se expandiera esfuerzo manual añadiendo nuevas fuentes de datos o manejando cambios de esquema: los datos simplemente aparecerían mágicamente en HDFS y las tablas Hive se generarían automáticamente para nuevas fuentes de datos con las columnas apropiadas.

Tercero, todavía teníamos una cobertura de datos muy baja. Es decir, si mirabas el porcentaje global de los datos que LinkedIn tenía y que estaban disponibles en Hadoop, todavía era muy incompleto. Y llegar a la finalización no iba a ser fácil dado el esfuerzo requerido para operacionalizar cada nueva fuente de datos.

La forma en que habíamos estado procediendo, creando cargas de datos personalizadas para cada fuente y destino de datos, era claramente inviable. Teníamos docenas de sistemas y repositorios de datos. Conectar todos estos habría llevado a construir tuberías personalizadas entre cada par de sistemas algo así como esto:

Logs

Ten en cuenta que los datos a menudo fluyen en ambas direcciones, ya que muchos sistemas (bases de datos, Hadoop) son tanto fuentes como destinos para la transferencia de datos. Esto significaba que terminaríamos construyendo dos pipelines por sistema: uno para meter datos y otro para sacar datos.

Claramente, esto requeriría un ejército de personas para construir y nunca sería operable. A medida que nos acercáramos a la conectividad total, terminaríamos con algo así como O(N2) pipelines.

En cambio, necesitábamos algo genérico como esto:

Logs

En la medida de lo posible, necesitábamos aislar a cada consumidor de la fuente de los datos. Idealmente deberían integrarse con un solo repositorio de datos que les diera acceso a todo.

La idea es que agregar un nuevo sistema de datos, ya sea una fuente de datos o un destino de datos, debería crear trabajo de integración solo para conectarlo a un único pipeline en lugar de cada consumidor de datos.

Esta experiencia me llevó a enfocarme en construir Kafka para combinar lo que habíamos visto en sistemas de mensajería con el concepto de log popular en bases de datos y en los internos de los sistemas distribuidos. Queríamos algo que actuara como un pipeline central primero para todos los datos de actividad, y eventualmente para muchos otros usos, incluida la implementación de datos fuera de Hadoop, datos de monitoreo, etc.

Durante mucho tiempo, Kafka fue un poco único (algunos dirían que extraño) como producto de infraestructura, ni una base de datos ni un sistema de colección de archivos de registro ni un sistema de mensajería tradicional. Pero recientemente Amazon ha ofrecido un servicio que es muy, muy similar a Kafka llamado Kinesis. La similitud llega incluso a la forma en que se manejan la partición de datos, se retienen los datos, y la bastante rara división en la API de Kafka entre consumidores de alto y bajo nivel. Estaba bastante feliz por esto. ¡Un signo de que has creado una buena abstracción de infraestructura es que AWS lo ofrece como servicio! Su visión para esto parece ser exactamente similar a lo que estoy describiendo: es la tubería que conecta todos sus sistemas distribuidos: DynamoDB, RedShift, S3, etc., así como la base para el procesamiento de flujos distribuidos usando EC2.

Relación con ETL y el Almacén de Datos

Hablemos un poco de almacenamiento de datos. El almacén de datos está destinado a ser un repositorio de datos limpios, integrados, estructurados para soportar el análisis. Esta es una gran idea. Para aquellos que no estén al tanto, la metodología de almacenamiento de datos implica extraer periódicamente datos de bases de datos de origen, transformarlos en alguna forma comprensible, y cargarlos en un almacén de datos central. Tener esta ubicación central que contiene una copia limpia de todos tus datos es un activo enormemente valioso para el análisis y procesamiento intensivo de datos. A un alto nivel, esta metodología no cambia demasiado si usas un almacén de datos tradicional como Oracle o Teradata o Hadoop, aunque podrías cambiar el orden de carga y transformación.

Un almacén de datos que contiene datos limpios e integrados es un activo fenomenal, pero la mecánica de obtener esto está un poco desactualizada.”

Logs

El problema clave para una organización centrada en los datos es vincular los datos limpios e integrados con el almacén de datos. Un almacén de datos es una infraestructura de consultas por lotes que es adecuada para muchos tipos de informes y análisis ad hoc, especialmente cuando las consultas involucran conteo simple, agregación y filtrado. Pero tener un sistema por lotes como el único repositorio de datos limpios y completos significa que los datos no están disponibles para sistemas que requieren un flujo en tiempo real: procesamiento en tiempo real, indexación de búsqueda, sistemas de monitoreo, etc.

Desde mi punto de vista, ETL es realmente dos cosas. Primero, es un proceso de extracción y limpieza de datos, esencialmente liberando datos atrapados en una variedad de sistemas en la organización y eliminando cualquier sin sentido específico del sistema. En segundo lugar, esos datos se reestructuran para consultas de almacén de datos (es decir, se ajustan al sistema de tipos de una DB relacional, se fuerzan en un esquema de estrella o copo de nieve, tal vez se dividen en un formato de columna de alto rendimiento, etc). Confluir estas dos cosas es un problema. El repositorio limpio e integrado de datos debe estar disponible en tiempo real también para procesamiento de baja latencia así como para indexación en otros sistemas de almacenamiento en tiempo real.

Creo que esto tiene el beneficio adicional de hacer que el ETL del almacén de datos sea mucho más escalable organizativamente. El problema clásico del equipo del almacén de datos es que son responsables de recoger y limpiar todos los datos generados por todos los demás equipos de la organización. Los incentivos no están alineados: los productores de datos a menudo no son muy conscientes del uso de los datos en el almacén de datos y terminan creando datos que son difíciles de extraer o requieren transformación pesada, difícil de escalar, para ponerlos en una forma utilizable. Por supuesto, el equipo central nunca logra escalar para igualar el ritmo del resto de la organización, por lo que la cobertura de datos siempre es irregular, el flujo de datos es frágil y los cambios son lentos.

Un enfoque mejor es tener un canal central, el log, con una API bien definida para agregar datos. La responsabilidad de integrarse con este canal y proporcionar un feed de datos limpio y bien estructurado recae en el productor de este feed de datos. Esto significa que como parte de su diseño e implementación de sistemas deben considerar el problema de sacar los datos y ponerlos en una forma bien estructurada para la entrega al canal central. La adición de nuevos sistemas de almacenamiento no tiene consecuencia alguna para el equipo del almacén de datos ya que tienen un punto central de integración. El equipo del almacén de datos maneja solo el problema más simple de cargar feeds estructurados de datos desde el log central y realizar transformaciones específicas para su sistema.

Logs

Este punto sobre la escalabilidad organizativa se vuelve particularmente importante cuando se considera adoptar sistemas de datos adicionales más allá de un almacén de datos tradicional. Digamos, por ejemplo, que uno desea proporcionar capacidades de búsqueda sobre el conjunto de datos completo de la organización. O, digamos que uno quiere proporcionar monitoreo de sub-segundos de flujos de datos con gráficos de tendencias y alertas en tiempo real. En cualquiera de estos casos, la infraestructura del almacén de datos tradicional o incluso un clúster de Hadoop va a ser inapropiada. Peor aún, la tubería de procesamiento de ETL construida para soportar cargas de bases de datos probablemente no sea útil para alimentar estos otros sistemas, haciendo que la implementación de estas infraestructuras sea tan grande como la adopción de un almacén de datos. Probablemente esto no sea factible y probablemente ayuda a explicar por qué la mayoría de las organizaciones no tienen estas capacidades fácilmente disponibles para todos sus datos. En contraste, si la organización hubiera construido feeds de datos uniformes y bien estructurados, dar acceso completo a todos los datos a cualquier nuevo sistema requiere solo un poco de plomería de integración para conectarse al canal.

Esta arquitectura también plantea un conjunto de diferentes opciones para dónde puede residir una limpieza o transformación particular:

  1. Puede ser realizado por el productor de datos antes de agregar los datos al log de la compañía.
  2. Puede hacerse como una transformación en tiempo real en el log (que a su vez produce un nuevo log transformado)
  3. Puede hacerse como parte del proceso de carga en algún sistema de destino de datos

El mejor modelo es tener limpieza realizada antes de publicar los datos en el log por el editor de los datos. Esto significa asegurarse de que los datos estén en una forma canónica y no retengan restos del código particular que los produjo o del sistema de almacenamiento en el que se pueden haber mantenido. Estos detalles son mejor manejados por el equipo que crea los datos ya que saben más sobre sus propios datos. Cualquier lógica aplicada en esta etapa debe ser sin pérdida y reversible.

Cualquier tipo de transformación que añada valor y que pueda realizarse en tiempo real debería hacerse como post-procesamiento en el feed de log crudo producido. Esto incluiría cosas como la sesionización de datos de eventos, o la adición de otros campos derivados que sean de interés general. El log original sigue estando disponible, pero este procesamiento en tiempo real puede producir un feed derivado que sea de interés general y esté disponible para cualquier sistema que lo desee.

Finalmente, solo la agregación que es específica para el sistema de destino debe realizarse como parte del proceso de carga. Esto podría incluir transformar datos en un esquema de estrella o copo de nieve particular para el análisis e informes en un almacén de datos. Debido a que esta etapa, que se relaciona más naturalmente con el proceso ETL tradicional, ahora se realiza en un conjunto de flujos mucho más limpio y uniforme, debería ser mucho más simplificada.

Archivos de Registro y Eventos

Hablemos un poco acerca de un beneficio secundario de esta arquitectura: permite sistemas impulsados por eventos y desacoplados.

El enfoque típico para los datos de actividad en la industria web es registrarlos en archivos de texto donde pueden ser arrastrados a un almacén de datos o a Hadoop para su agregación y consulta. El problema con esto es el mismo que con todo el ETL por lotes: acopla el flujo de datos a las capacidades y al horario de procesamiento del almacén de datos.

En LinkedIn, hemos construido nuestro manejo de datos de eventos de una manera centrada en los registros. Estamos usando Kafka como el registro de eventos central con múltiples suscriptores. Hemos definido varios cientos de tipos de eventos, cada uno capturando los atributos únicos sobre un tipo particular de acción. Esto cubre todo, desde vistas de página, impresiones de anuncios y búsquedas, hasta invocaciones de servicio y excepciones de aplicación.

Para entender las ventajas de esto, imagine un evento simple: mostrar una oferta de trabajo en la página de empleo. La página de empleo debería contener solo la lógica necesaria para mostrar el trabajo. Sin embargo, en un sitio bastante dinámico, esto podría fácilmente volverse cargado con lógica adicional no relacionada con mostrar el trabajo. Por ejemplo, digamos que necesitamos integrar los siguientes sistemas:

  1. Necesitamos enviar estos datos a Hadoop y al almacén de datos para fines de procesamiento fuera de línea.
  2. Necesitamos contar la vista para asegurarnos de que el espectador no esté intentando algún tipo de scraping de contenido.
  3. Necesitamos agregar esta vista para mostrarla en la página de análisis del publicador de la oferta.
  4. Necesitamos registrar la vista para asegurarnos de limitar correctamente cualquier impresión de recomendaciones de trabajo para ese usuario (no queremos mostrar lo mismo una y otra vez).
  5. Nuestro sistema de recomendación puede necesitar registrar la vista para seguir correctamente la popularidad de esa oferta de trabajo.
  6. Etcétera.

Pronto, el simple acto de mostrar un trabajo se ha vuelto bastante complejo. Y a medida que agregamos otros lugares donde se muestran los trabajos —aplicaciones móviles, etc.—, esta lógica debe ser trasladada y la complejidad aumenta. Peor aún, los sistemas con los que necesitamos interfaz ahora están algo entrelazados: la persona que trabaja en mostrar trabajos necesita saber acerca de muchos otros sistemas y características y asegurarse de que estén integrados correctamente. Esta es solo una versión de juguete del problema; cualquier aplicación real sería más, no menos, compleja.

El estilo “impulsado por eventos” proporciona un enfoque para simplificar esto. La página de visualización de trabajo ahora solo muestra un trabajo y registra el hecho de que se mostró un trabajo junto con los atributos relevantes del trabajo, el espectador y cualquier otro hecho útil sobre la muestra del trabajo. Cada uno de los otros sistemas interesados —el sistema de recomendación, el sistema de seguridad, el sistema de análisis del publicador de ofertas y el almacén de datos— simplemente se suscribe al feed y realiza su procesamiento. El código de visualización no necesita estar al tanto de estos otros sistemas y no necesita ser cambiado si se añade un nuevo consumidor de datos.

Construyendo un Registro Escalable

Por supuesto, separar a los publicadores de los suscriptores no es algo nuevo. Pero si quieres mantener un registro de compromisos que actúe como un diario en tiempo real con múltiples suscriptores de todo lo que sucede en un sitio web a escala de consumidor, la escalabilidad será un desafío primordial. Usar un registro como un mecanismo de integración universal nunca va a ser más que una fantasía elegante si no podemos construir un registro que sea rápido, económico y lo suficientemente escalable para hacerlo práctico a gran escala.

Las personas de sistemas típicamente piensan en un registro distribuido como una abstracción lenta y pesada (y generalmente la asocian solo con el tipo de usos de “metadatos” para los cuales Zookeeper podría ser apropiado). Pero con una implementación reflexiva enfocada en el registro de grandes flujos de datos, esto no tiene por qué ser cierto. En LinkedIn, actualmente estamos ejecutando más de 60 mil millones de escrituras de mensajes únicos a través de Kafka por día (varios cientos de miles de millones si cuentas las escrituras de espejeo entre centros de datos).

Usamos algunos trucos en Kafka para soportar este tipo de escala:

  1. Particionando el registro
  2. Optimizando el rendimiento agrupando lecturas y escrituras
  3. Evitando copias de datos innecesarias

Para permitir la escalabilidad horizontal, dividimos nuestro registro en particiones:

Logs

Cada partición es un registro totalmente ordenado, pero no hay un orden global entre las particiones (aparte de quizás algún tiempo de reloj de pared que podrías incluir en tus mensajes). La asignación de los mensajes a una partición particular es controlable por el escritor, con la mayoría de los usuarios eligiendo particionar por algún tipo de clave (p.ej., id de usuario). La particionamiento permite que las adiciones al registro ocurran sin coordinación entre los fragmentos y permite que el rendimiento del sistema escale linealmente con el tamaño del clúster de Kafka.

Cada partición está replicada en un número configurable de réplicas, cada una de las cuales tiene una copia idéntica del registro de la partición. En cualquier momento, una de ellas actuará como líder; si el líder falla, una de las réplicas tomará el liderazgo.

La falta de un orden global a través de las particiones es una limitación, pero no la hemos encontrado como una mayor. De hecho, la interacción con el registro típicamente proviene de cientos o miles de procesos distintos por lo que no es significativo hablar de un orden total sobre su comportamiento. En cambio, las garantías que proporcionamos son que cada partición preserva el orden, y Kafka garantiza que las adiciones a una partición particular de un único remitente se entregarán en el orden en que se envían.

Un registro, como un sistema de archivos, es fácil de optimizar para patrones de lectura y escritura lineales. El registro puede agrupar pequeñas lecturas y escrituras en operaciones más grandes y de alto rendimiento. Kafka persigue esta optimización agresivamente. El agrupamiento ocurre desde el cliente al servidor al enviar datos, en escrituras a disco, en replicación entre servidores, en transferencia de datos a consumidores, y en reconocimiento de datos comprometidos.

Finalmente, Kafka utiliza un formato binario simple que se mantiene entre el registro en memoria, el registro en disco, y las transferencias de datos en red. Esto nos permite hacer uso de numerosas optimizaciones, incluyendo transferencia de datos sin copia.

El efecto acumulativo de estas optimizaciones es que generalmente puedes escribir y leer datos a la velocidad que el disco o la red permitan, incluso mientras se mantienen conjuntos de datos que exceden ampliamente la memoria.

Esta redacción no está destinada a ser principalmente sobre Kafka, así que no entraré en más detalles. Puedes leer una visión más detallada del enfoque de LinkedIn aquí y una visión completa del diseño de Kafka aquí.

Parte Tres: Registros y Procesamiento de Flujo en Tiempo Real

Hasta ahora, sólo he descrito lo que equivale a un método sofisticado de copiar datos de un lugar a otro. Pero trasladar bytes entre sistemas de almacenamiento no es el final de la historia. Resulta que “registro” es otra palabra para “flujo” y los registros están en el corazón del procesamiento de flujo.

Pero, espera, ¿qué es exactamente el procesamiento de flujo?

Si eres fanático de la literatura y productos de bases de datos de finales de los 90 y principios de los 2000, probablemente asocias el procesamiento de flujo con esfuerzos para construir un motor SQL o una interfaz de “cajas y flechas” para el procesamiento impulsado por eventos.

Si sigues la explosión de los sistemas de datos de código abierto, probablemente asocies el procesamiento de flujo con algunos de los sistemas en este espacio, por ejemplo, Storm, Akka, S4, y Samza. Pero la mayoría de la gente ve esto como una especie de sistema de procesamiento de mensajes asíncrono no muy diferente de una capa RPC consciente del clúster (y de hecho algunas cosas en este espacio son exactamente eso).

Ambas visiones son un poco limitadas. El procesamiento de flujo no tiene nada que ver con SQL. Tampoco está limitado al procesamiento en tiempo real. No hay ninguna razón inherente por la que no puedas procesar el flujo de datos de ayer o de hace un mes utilizando una variedad de lenguajes diferentes para expresar el cálculo.

Logs

Veo el procesamiento de flujo como algo mucho más amplio: infraestructura para el procesamiento continuo de datos. Creo que el modelo computacional puede ser tan general como MapReduce u otros marcos de procesamiento distribuido, pero con la capacidad de producir resultados de baja latencia.

El verdadero impulsor para el modelo de procesamiento es el método de recolección de datos. Los datos que se recogen en lotes se procesan naturalmente en lotes. Cuando los datos se recogen continuamente, se procesan naturalmente de manera continua.

El censo de EE. UU. proporciona un buen ejemplo de recolección de datos por lotes. El censo se inicia periódicamente y realiza un descubrimiento y enumeración de ciudadanos estadounidenses de manera bruta, teniendo personas que caminan de puerta en puerta. Esto tenía mucho sentido en 1790 cuando se inició el censo. La recolección de datos en ese momento era inherentemente por lotes, implicaba montar a caballo y escribir registros en papel, luego transportar este lote de registros a una ubicación central donde los humanos sumaban todos los recuentos. Hoy en día, cuando describes el proceso del censo, uno se pregunta inmediatamente por qué no mantenemos un registro de nacimientos y muertes y producimos recuentos de población ya sea de manera continua o con la granularidad que se necesite.

Este es un ejemplo extremo, pero muchos procesos de transferencia de datos aún dependen de la realización de volcados periódicos y la transferencia e integración masivas. La única manera natural de procesar un volcado masivo es con un proceso por lotes. Pero a medida que estos procesos son reemplazados por flujos continuos, uno naturalmente comienza a moverse hacia el procesamiento continuo para suavizar los recursos de procesamiento necesarios y reducir la latencia.

LinkedIn, por ejemplo, casi no tiene recolección de datos por lotes en absoluto. La mayoría de nuestros datos son datos de actividad o cambios en la base de datos, ambos de los cuales ocurren continuamente. De hecho, cuando piensas en cualquier negocio, la mecánica subyacente es casi siempre un proceso continuo: los eventos ocurren en tiempo real, como Jack Bauer nos diría. Cuando los datos se recogen en lotes, casi siempre se debe a algún paso manual o falta de digitalización o es un vestigio histórico que queda de la automatización de algún proceso no digital. Transmitir y reaccionar a los datos solía ser muy lento cuando la mecánica era el correo y los humanos hacían el procesamiento. Un primer intento de automatización siempre retiene la forma del proceso original, por lo que esto a menudo perdura durante mucho tiempo.

Los trabajos de procesamiento “por lotes” de producción que se ejecutan diariamente a menudo están imitando efectivamente un tipo de cálculo continuo con un tamaño de ventana de un día. Los datos subyacentes, por supuesto, siempre están cambiando. Estos eran tan comunes en LinkedIn (y la mecánica para hacerlos funcionar en Hadoop tan complicada) que implementamos todo un marco para gestionar los flujos de trabajo incrementales de Hadoop.

Visto de esta manera, es fácil tener una visión diferente del procesamiento de flujo: es simplemente procesamiento que incluye una noción de tiempo en los datos subyacentes que se están procesando y no requiere una instantánea estática de los datos, por lo que puede producir salida a una frecuencia controlada por el usuario en lugar de esperar que se alcance el “final” del conjunto de datos. En este sentido, el procesamiento de flujo es una generalización del procesamiento por lotes y, dada la prevalencia de los datos en tiempo real, una generalización muy importante.

Entonces, ¿por qué la visión tradicional del procesamiento de flujo ha sido como una aplicación de nicho? Creo que la mayor razón es que la falta de recolección de datos en tiempo real hizo que el procesamiento continuo fuera algo de una preocupación académica.

Creo que la falta de recolección de datos en tiempo real es probablemente lo que condenó a los sistemas comerciales de procesamiento de flujo. Sus clientes aún estaban realizando procesamiento por lotes de archivos diarios para ETL e integración de datos. Las empresas que construían sistemas de procesamiento de flujo se centraban en proporcionar motores de procesamiento para adjuntar a flujos de datos en tiempo real, pero resultó que en ese momento muy pocas personas realmente tenían flujos de datos en tiempo real. De hecho, muy temprano en mi carrera en LinkedIn, una empresa intentó vendernos un sistema de procesamiento de flujo muy interesante, pero dado que todos nuestros datos se recopilaban en archivos por hora en ese momento, ¡la mejor aplicación que pudimos encontrar fue canalizar los archivos por hora en el sistema de flujo al final de la hora! Notaron que este era un problema bastante común. La excepción en realidad demuestra la regla aquí: las finanzas, el único dominio donde el procesamiento de flujo ha tenido algo de éxito, era exactamente el área donde los flujos de datos en tiempo real ya eran la norma y el procesamiento se había convertido en el cuello de botella.

Incluso en presencia de un ecosistema de procesamiento por lotes saludable, creo que la aplicabilidad real del procesamiento de flujo como estilo de infraestructura es bastante amplia. Creo que cubre la brecha en la infraestructura entre los servicios de solicitud/respuesta en tiempo real y el procesamiento por lotes fuera de línea. Para las modernas empresas de internet, creo que alrededor del 25% de su código cae en esta categoría.

Resulta que el log resuelve algunos de los problemas técnicos más críticos en el procesamiento de flujo, que describiré, pero el mayor problema que resuelve es simplemente hacer que los datos estén disponibles en flujos de datos en tiempo real para múltiples suscriptores. Para aquellos interesados en más detalles, hemos liberado el código de Samza, un sistema de procesamiento de flujo construido explícitamente sobre muchas de estas ideas. Describimos muchas de estas aplicaciones con más detalle en la documentación aquí.

Grafos de flujo de datos

Logs

El aspecto más interesante del procesamiento de flujo no tiene nada que ver con los aspectos internos de un sistema de procesamiento de flujo, sino que tiene que ver con cómo extiende nuestra idea de lo que es un feed de datos desde la discusión anterior sobre integración de datos. Hablamos principalmente de feeds o logs de datos primarios, los eventos y filas de datos producidos en la ejecución de varias aplicaciones. Pero el procesamiento de flujo nos permite también incluir feeds calculados a partir de otros feeds. Estos feeds derivados no se ven diferentes para los consumidores que los feeds de datos primarios de los que se calculan. Estos feeds derivados pueden encapsular una complejidad arbitraria.

Profundicemos un poco en esto. Un trabajo de procesamiento de flujo, para nuestros propósitos, será cualquier cosa que lea de logs y escriba la salida a logs u otros sistemas. Los logs que usan para entrada y salida unen estos procesos en un grafo de etapas de procesamiento. De hecho, utilizando un log centralizado de esta manera, se puede ver toda la captura de datos de la organización, transformación y flujo como simplemente una serie de logs y procesos que escriben en ellos.

Un procesador de flujo no necesita tener un marco de trabajo sofisticado en absoluto: puede ser cualquier proceso o conjunto de procesos que lean y escriban desde logs, pero se puede proporcionar infraestructura y soporte adicionales para ayudar a gestionar el código de procesamiento.

El propósito del log en la integración es doble.

Primero, hace que cada conjunto de datos sea multi-suscriptor y ordenado. Recuerde nuestro principio de “replicación de estado” para recordar la importancia del orden. Para hacer esto más concreto, considere un flujo de actualizaciones de una base de datos; si reordenamos dos actualizaciones al mismo registro en nuestro procesamiento, podemos producir la salida final incorrecta. Este orden es más permanente que lo que proporciona algo como TCP ya que no está limitado a un único enlace punto a punto y sobrevive más allá de fallos de procesos y reconexiones.

En segundo lugar, el log proporciona buffering a los procesos. Esto es muy fundamental. Si el procesamiento procede de una manera no sincronizada, es probable que ocurra que un trabajo productor de datos de corriente arriba produzca datos más rápidamente de lo que otro trabajo de corriente abajo puede consumirlos. Cuando esto ocurre, el procesamiento debe bloquearse, almacenar en búfer o descartar datos. Descartar datos probablemente no sea una opción; bloquear puede causar que todo el grafo de procesamiento se detenga. El log actúa como un buffer muy, muy grande que permite que el proceso se reinicie o falle sin ralentizar otras partes del grafo de procesamiento. Este aislamiento es particularmente importante cuando se extiende este flujo de datos a una organización más grande, donde el procesamiento está siendo realizado por trabajos creados por muchos equipos diferentes. No podemos tener un trabajo defectuoso que cause presión de retorno que detenga todo el flujo de procesamiento.

Tanto Storm como Samza están construidos de esta manera y pueden usar Kafka u otros sistemas similares como su log.

Procesamiento en Tiempo Real con Estado

Algunos procesamientos de flujo en tiempo real son solo transformaciones sin estado de registro por registro, pero muchos de los usos son conteos más sofisticados, agregaciones o uniones sobre ventanas en el flujo. Uno podría, por ejemplo, querer enriquecer un flujo de eventos (digamos, un flujo de clics) con información sobre el usuario que hace el clic, en efecto, uniendo el flujo de clics a la base de datos de cuentas de usuario. Invariablemente, este tipo de procesamiento termina requiriendo que se mantenga algún tipo de estado por el procesador: por ejemplo, al calcular un conteo, hay que mantener el conteo hasta ahora. ¿Cómo se puede mantener este tipo de estado correctamente si los propios procesadores pueden fallar?

La alternativa más simple sería mantener el estado en memoria. Sin embargo, si el proceso se bloquea, perdería su estado intermedio. Si el estado solo se mantiene a lo largo de una ventana, el proceso podría simplemente retroceder al punto en el log donde comenzó la ventana. Sin embargo, si se está haciendo un conteo durante una hora, esto puede no ser factible.

Una alternativa es simplemente almacenar todo el estado en un sistema de almacenamiento remoto y unirlo a través de la red a esa tienda. El problema con esto es que no hay localidad de datos y muchos viajes redondos de la red.

¿Cómo podemos soportar algo como una “tabla” que está particionada con nuestro procesamiento?

Recuerden la discusión sobre la dualidad de las tablas y los logs. Esto nos da exactamente la herramienta para poder convertir flujos en tablas co-localizadas con nuestro procesamiento, así como un mecanismo para manejar la tolerancia a fallos para estas tablas.

Un procesador de flujo puede mantener su estado en una “tabla” o “índice” local: un bdb, leveldb, o incluso algo más inusual como un índice Lucene o fastbit. El contenido de este almacenamiento es alimentado desde sus flujos de entrada (después de aplicar quizás primero una transformación arbitraria). Puede registrar un log de cambios para este índice local que mantiene para permitirle restaurar su estado en caso de un fallo y reinicio. Este mecanismo permite un mecanismo genérico para mantener el estado co-particionado en tipos de índices arbitrarios locales con los datos de flujo entrantes.

Cuando el proceso falla, restaura su índice desde el log de cambios. El log es la transformación del estado local en una especie de copia de seguridad incremental registro por registro.

Este enfoque para la gestión del estado tiene la elegante propiedad de que el estado de los procesadores también se mantiene como un log. Podemos pensar en este log justo como lo haríamos con el log de cambios en una tabla de base de datos. De hecho, los procesadores tienen algo muy parecido a una tabla co-particionada mantenida junto a ellos. Dado que este estado es en sí mismo un log, otros procesadores pueden suscribirse a él. Esto puede ser realmente útil en casos en que el objetivo del procesamiento es actualizar un estado final y este estado es la salida natural del procesamiento.

Cuando se combina con los logs que salen de las bases de datos con fines de integración de datos, el poder de la dualidad log/tabla se hace evidente. Un log de cambios puede ser extraído de una base de datos e indexado en diferentes formas por varios procesadores de flujo para unirse contra flujos de eventos.

Damos más detalles sobre este estilo de gestión del procesamiento con estado en Samza y muchos más ejemplos prácticos aquí.

Compactación de Logs

Logs

Por supuesto, no podemos esperar mantener un log completo de todos los cambios de estado por siempre. A menos que se quiera usar un espacio infinito, de alguna manera el log debe ser limpiado. Hablaré un poco sobre la implementación de esto en Kafka para hacerlo más concreto. En Kafka, la limpieza tiene dos opciones dependiendo de si los datos contienen actualizaciones con clave o datos de eventos. Para datos de eventos, Kafka admite simplemente retener una ventana de datos. Usualmente, esto se configura para unos pocos días, pero la ventana puede definirse en términos de tiempo o espacio. Para datos con clave, sin embargo, una buena propiedad del log completo es que puedes reproducirlo para recrear el estado del sistema fuente (potencialmente recreándolo en otro sistema).

Sin embargo, retener el log completo utilizará más y más espacio a medida que pasa el tiempo, y la reproducción llevará cada vez más tiempo. Por lo tanto, en Kafka, admitimos un tipo diferente de retención. En lugar de simplemente desechar el log antiguo, eliminamos registros obsoletos, es decir, registros cuya clave primaria tiene una actualización más reciente. Al hacer esto, aún garantizamos que el log contiene una copia de seguridad completa del sistema fuente, pero ahora ya no podemos recrear todos los estados previos del sistema fuente, solo los más recientes. Llamamos a esta característica compactación de log.

Parte Cuatro: Construcción de Sistemas

El último tema que quiero discutir es el papel del log en el diseño del sistema de datos para sistemas de datos en línea.

Hay una analogía aquí entre el papel que un log sirve para el flujo de datos dentro de una base de datos distribuida y el papel que desempeña para la integración de datos en una organización más grande. En ambos casos, es responsable del flujo de datos, la coherencia y la recuperación. Después de todo, ¿qué es una organización, si no un sistema de datos distribuido muy complicado?

¿Desagregación?

Así que tal vez si entrecierras un poco los ojos, puedes ver todo el sistema y flujos de datos de tu organización como una única base de datos distribuida. Puedes ver todos los sistemas individuales orientados a consultas (Redis, SOLR, tablas Hive, etc.) como simples índices particulares en tus datos. Puedes ver los sistemas de procesamiento de flujos como Storm o Samza como solo un mecanismo de disparo y materialización de vistas muy bien desarrollado. La gente clásica de bases de datos, he notado, le gusta mucho esta visión porque finalmente les explica qué diablos está haciendo la gente con todos estos diferentes sistemas de datos: ¡son solo diferentes tipos de índices!

Indudablemente, ahora hay una explosión de tipos de sistemas de datos, pero en realidad, esta complejidad siempre ha existido. ¡Incluso en la época dorada de la base de datos relacional, las organizaciones tenían muchas y muchas bases de datos relacionales! Así que tal vez la integración real no ha existido desde la época de la mainframe, cuando todos los datos realmente estaban en un lugar. Hay muchas motivaciones para segregar datos en múltiples sistemas: escala, geografía, seguridad y aislamiento del rendimiento son los más comunes. Pero estos problemas pueden ser abordados por un buen sistema: es posible que una organización tenga un único clúster de Hadoop, por ejemplo, que contenga todos los datos y sirva a una gran y diversa comunidad.

Así que ya existe una posible simplificación en el manejo de datos que se ha vuelto posible en la transición a sistemas distribuidos: fusionar muchas instancias pequeñas de cada sistema en unos pocos clústeres grandes. Muchos sistemas aún no son lo suficientemente buenos para permitir esto: no tienen seguridad, o no pueden garantizar el aislamiento del rendimiento, o simplemente no escalan lo suficientemente bien. Pero cada uno de estos problemas es solucionable.

Mi opinión es que la explosión de diferentes sistemas es causada por la dificultad de construir sistemas de datos distribuidos. Al reducirse a un solo tipo de consulta o caso de uso, cada sistema es capaz de reducir su alcance al conjunto de cosas que se pueden construir. Pero ejecutar todos estos sistemas produce demasiada complejidad.

Veo tres posibles direcciones que esto podría seguir en el futuro.

La primera posibilidad es una continuación del status quo: la separación de los sistemas permanece más o menos como está por mucho más tiempo. Esto podría suceder porque la dificultad de la distribución es demasiado difícil de superar o porque esta especialización permite nuevos niveles de conveniencia y poder para cada sistema. Mientras esto siga siendo cierto, el problema de la integración de datos seguirá siendo una de las cosas más centralmente importantes para el uso exitoso de los datos. En este caso, un log externo que integre datos será muy importante.

La segunda posibilidad es que podría haber una reconsolidación en la que un único sistema con suficiente generalidad comience a fusionar nuevamente todas las diferentes funciones en un único súper sistema. Este súper sistema podría ser como la base de datos relacional superficialmente, pero su uso en una organización sería muy diferente ya que solo necesitarías uno grande en lugar de muchos pequeños. En este mundo, no hay un verdadero problema de integración de datos excepto lo que se resuelve dentro de este sistema. Creo que las dificultades prácticas para construir tal sistema hacen que esto sea poco probable.

Sin embargo, hay otro posible resultado, que de hecho encuentro atractivo como ingeniero. Una faceta interesante de la nueva generación de sistemas de datos es que casi todos son de código abierto. El código abierto permite otra posibilidad: la infraestructura de datos podría desagregarse en una colección de servicios y APIs de sistemas orientados a aplicaciones. Ya ves que esto sucede hasta cierto punto en el stack de Java:

Si apilas estas cosas y entrecierras un poco los ojos, comienza a parecerse un poco a una versión de Lego de la ingeniería de sistemas de datos distribuidos. Puedes unir estos ingredientes para crear una vasta gama de sistemas posibles. Claramente, esta no es una historia relevante para los usuarios finales, quienes presumiblemente se preocupan principalmente más por la API que por cómo está implementada, pero podría ser un camino hacia la obtención de la simplicidad del sistema único en un mundo más diverso y modular que continúa evolucionando. Si el tiempo de implementación para un sistema distribuido pasa de años a semanas porque emergen bloques de construcción fiables y flexibles, entonces la presión para coalescer en un único sistema monolítico desaparece.

El lugar del log en la arquitectura del sistema

Un sistema que asume que hay un log externo presente permite que los sistemas individuales renuncien a mucha de su propia complejidad y confíen en el log compartido. Aquí están las cosas que creo que un log puede hacer:

  • Manejar la consistencia de datos (ya sea eventual o inmediata) secuenciando actualizaciones concurrentes en los nodos
  • Proporcionar replicación de datos entre nodos
  • Proporcionar semánticas de “commit” al escritor (es decir, reconocer solo cuando se garantiza que tu escritura no se perderá)
  • Proporcionar la fuente de suscripción de datos externos del sistema
  • Proporcionar la capacidad de restaurar réplicas fallidas que perdieron sus datos o iniciar nuevas réplicas
  • Manejar el reequilibrio de datos entre nodos.

Esta es en realidad una parte sustancial de lo que hace un sistema de datos distribuido. De hecho, la mayoría de lo que queda está relacionado con la API de consulta final que enfrenta al cliente y la estrategia de indexación. Esta es exactamente la parte que debería variar de un sistema a otro: por ejemplo, una consulta de búsqueda de texto completo puede necesitar consultar todas las particiones, mientras que una consulta por clave primaria puede solo necesitar consultar un único nodo responsable de los datos de esa clave.

Así es como funciona. El sistema se divide en dos partes lógicas: el log y la capa de servicio. El log captura los cambios de estado en orden secuencial. Los nodos de servicio almacenan cualquier índice que se requiera para atender consultas (por ejemplo, un almacén de clave-valor podría tener algo como un btree o sstable, un sistema de búsqueda tendría un índice invertido). Las escrituras pueden ir directamente al log, aunque pueden ser proxy mediante la capa de servicio. Escribir en el log produce una marca de tiempo lógica (digamos el índice en el log). Si el sistema está particionado, y asumo que lo está, entonces el log y los nodos de servicio tendrán el mismo número de particiones, aunque pueden tener números muy diferentes de máquinas.

Logs

Los nodos de servicio se suscriben al log y aplican las escrituras tan rápido como es posible en su índice local en el orden en que el log los ha almacenado.

El cliente puede obtener semánticas de leer-tu-escritura desde cualquier nodo proporcionando la marca de tiempo de una escritura como parte de su consulta: un nodo de servicio que reciba dicha consulta comparará la marca de tiempo deseada con su propio punto de índice y, si es necesario, retrasará la solicitud hasta que haya indexado al menos hasta ese momento para evitar servir datos obsoletos.

Los nodos de servicio pueden o no necesitar tener alguna noción de “maestría” o “elección de líder”. Para muchos casos de uso simples, los nodos de servicio pueden estar completamente sin líderes, ya que el log es la fuente de verdad.

Una de las cosas más delicadas que un sistema distribuido debe hacer es manejar la restauración de nodos fallidos o mover particiones de nodo a nodo. Un enfoque típico tendría al log reteniendo solo una ventana fija de datos y combinándolo con una instantánea de los datos almacenados en la partición. Es igualmente posible que el log retenga una copia completa de los datos y recolecte la basura del propio log. Esto traslada una cantidad significativa de complejidad fuera de la capa de servicio, que es específica del sistema, y hacia el log, que puede ser de propósito general.

Al tener este sistema de log, obtienes una API de suscripción completamente desarrollada para los contenidos del almacén de datos que alimenta ETL en otros sistemas. De hecho, muchos sistemas pueden compartir el mismo log mientras proporcionan diferentes índices, así:

Logs

Nota cómo un sistema tan centrado en los logs es inmediatamente un proveedor de flujos de datos para su procesamiento y carga en otros sistemas. De igual manera, un procesador de flujos puede consumir múltiples flujos de entrada y luego servirlos a través de otro sistema que indexe esa salida.

Encuentro que esta visión de los sistemas, descompuesta en un log y una API de consulta, es muy reveladora, ya que te permite separar las características de la consulta de los aspectos de disponibilidad y consistencia del sistema. Realmente creo que esta es incluso una forma útil de factorizar mentalmente un sistema que no está construido de esta manera para entenderlo mejor.

Vale la pena señalar que aunque Kafka y Bookeeper son logs consistentes, esto no es un requisito. Podrías fácilmente factorizar una base de datos al estilo Dynamo en un log AP eventualmente consistente y una capa de servicio de clave-valor. Trabajar con un log así es un poco complicado, ya que volverá a entregar mensajes antiguos y depende del suscriptor para manejar esto (mucho como el propio Dynamo).

La idea de tener una copia separada de los datos en el log (especialmente si es una copia completa) parece un derroche para muchas personas. En realidad, aunque hay algunos factores que hacen que esto sea menos problemático. Primero, el log puede ser un mecanismo de almacenamiento particularmente eficiente. Almacenamos más de 75TB por centro de datos en nuestros servidores Kafka de producción. Mientras que muchos sistemas de servicio requieren mucho más memoria para servir datos eficientemente (la búsqueda de texto, por ejemplo, a menudo está toda en memoria). El sistema de servicio también puede usar hardware optimizado. Por ejemplo, la mayoría de nuestros sistemas de datos en vivo sirven desde la memoria o usan SSDs. En contraste, el sistema de logs sólo realiza lecturas y escrituras lineales, por lo que está bastante contento usando grandes discos duros multi-TB. Finalmente, como en la imagen de arriba, en el caso donde los datos son servidos por múltiples sistemas, el costo del log se amortiza sobre múltiples índices. Esta combinación hace que el gasto de un log externo sea bastante mínimo.

Este es exactamente el patrón que LinkedIn ha usado para construir muchos de sus propios sistemas de consulta en tiempo real. Estos sistemas se alimentan de una base de datos (usando Databus como una abstracción de log o un log dedicado de Kafka) y proporcionan una partición, indexación y capacidad de consulta particulares sobre ese flujo de datos. Esta es la forma en que hemos implementado nuestros sistemas de búsqueda, grafo social y consultas OLAP. De hecho, es bastante común tener un único flujo de datos (ya sea un flujo en vivo o un flujo derivado que viene de Hadoop) replicado en múltiples sistemas de servicio para el servicio en vivo. Esto ha demostrado ser una suposición enormemente simplificadora. Ninguno de estos sistemas necesita tener una API de escritura accesible externamente en absoluto, Kafka y las bases de datos se utilizan como el sistema de registro y los cambios fluyen hacia los sistemas de consulta apropiados a través de ese log. Las escrituras son manejadas localmente por los nodos que hospedan una partición particular. Estos nodos transcriben ciegamente el feed proporcionado por el log a su propia tienda. Un nodo fallido puede ser restaurado reproduciendo el log upstream.

El grado en que estos sistemas dependen del log varía. Un sistema completamente dependiente podría hacer uso del log para la partición de datos, restauración de nodos, reequilibrio y todos los aspectos de consistencia y propagación de datos. En esta configuración, el tier de servicio real no es nada menos que una especie de “caché” estructurada para permitir un tipo particular de procesamiento con escrituras que van directamente al log.

El Final

Si llegaste hasta aquí, conoces la mayoría de lo que sé sobre registros (logs).

Aquí hay algunas referencias interesantes que podrías querer revisar.

Todos parecen usar diferentes términos para las mismas cosas, así que es un poco un rompecabezas conectar la literatura de bases de datos con los sistemas distribuidos, con los diversos campos del software empresarial, y con el mundo de código abierto. No obstante, aquí hay algunas indicaciones en la dirección general.

Artículos académicos, sistemas, charlas y blogs:

  • Una buena visión general de la replicación de máquina de estado y primario-respaldo.

  • PacificA es un marco genérico para implementar sistemas de almacenamiento distribuido basados en registros en Microsoft.

  • Spanner: No a todos les encanta el tiempo lógico para sus registros. La nueva base de datos de Google intenta usar el tiempo físico y modela la incertidumbre del desvío del reloj directamente al tratar la estampa de tiempo como un rango.

  • Datanomic: Deconstructing the database es una gran presentación de Rich Hickey, el creador de Clojure, sobre el producto de base de datos de su startup.

  • A Survey of Rollback-Recovery Protocols in Message-Passing Systems. Encontré que esta es una introducción muy útil para la tolerancia a fallos y la aplicación práctica de registros para la recuperación fuera de las bases de datos.

  • Reactive Manifesto: En realidad, no estoy del todo seguro de lo que se entiende por programación reactiva, pero creo que significa lo mismo que “guiada por eventos”. Este enlace no tiene mucha información, pero esta clase de Martin Odersky (famoso por Scala) parece fascinante.

  • ¡Paxos!

    • El artículo original está aquí. Leslie Lamport tiene una interesante historia de cómo el algoritmo fue creado en la década de 1980 pero no fue publicado hasta 1998 porque a los revisores no les gustó la parábola griega en el papel y él no quería cambiarla.
    • Aun después de que se publicó el artículo original, no fue bien comprendido. Lamport lo intenta de nuevo e incluso incluye algunos de los “detalles no interesantes” sobre cómo usarlo con estos nuevos computadores automáticos. Todavía no es ampliamente entendido.
    • Fred Schneider y Butler Lampson dan una visión más detallada de la aplicación de Paxos en sistemas reales.
    • Algunos ingenieros de Google resumen su experiencia implementando Paxos en Chubby.
    • En realidad, encontré todos los artículos de Paxos bastante difíciles de entender pero luché diligentemente a través de ellos. Pero no necesitas hacerlo porque este video de John Ousterhout (¡famoso por el sistema de archivos estructurado por registros!) lo hará todo muy simple. De alguna manera, estos algoritmos de consenso son mucho mejor presentados dibujándolos a medida que se desarrollan las rondas de comunicación, en lugar de en una presentación estática en un papel. Irónicamente, este video fue creado en un intento de mostrar que Paxos era difícil de entender.
    • Using Paxos to Build a Scalable Consistent Data Store: Este es un artículo genial sobre el uso de un registro para construir una tienda de datos, por Jun, uno de los coautores también es uno de los primeros ingenieros en Kafka.
  • ¡Paxos tiene competidores! De hecho, cada uno de estos se mapea mucho más estrechamente a la implementación de un log y probablemente son más adecuados para la implementación práctica:

    • Viewstamped Replication de Barbara Liskov es un algoritmo temprano para modelar directamente la replicación de logs.
    • Zab es el algoritmo utilizado por Zookeeper.
    • RAFT es un intento de crear un algoritmo de consenso más comprensible. La presentación en video, también de John Ousterhout, es estupenda también.
  • Puedes ver el rol del log en acción en diferentes bases de datos distribuidas reales.

    • PNUTS es un sistema que intenta aplicar el diseño centrado en logs de las bases de datos distribuidas tradicionales a gran escala.
    • HBase y Bigtable brindan otro ejemplo de logs en bases de datos modernas.
    • La propia base de datos distribuida de LinkedIn Espresso, como PNUTs, utiliza un log para la replicación, pero toma un enfoque ligeramente diferente utilizando la propia tabla subyacente como fuente del log.
  • Si te encuentras comparando opciones para un algoritmo de replicación, este artículo puede ayudarte.

  • Replication: Theory and Practice es un gran libro que recopila varios artículos resumen sobre replicación en sistemas distribuidos. Muchos de los capítulos están en línea (por ej., 1, 4, 5, 6, 7, 8).

  • Procesamiento de streams. Este es un tema un poco demasiado amplio para resumir, pero aquí hay algunas cosas que me gustaron.

El software empresarial tiene todos los mismos problemas pero con nombres diferentes, una escala menor y XML. Ja ja, es broma. Más o menos.

  • Event Sourcing — Hasta donde puedo decir, esto es básicamente la manera en que el ingeniero de software empresarial dice “replicación de máquina de estado”. Es interesante que la misma idea sea inventada nuevamente en un contexto tan diferente. Event Sourcing parece enfocarse en casos de uso más pequeños, en memoria. Este enfoque para el desarrollo de aplicaciones parece combinar el “procesamiento de stream” que ocurre en el log de eventos con la aplicación. Dado que esto se vuelve bastante no trivial cuando el procesamiento es lo suficientemente grande como para requerir la partición de datos para escalar, me enfoco en el procesamiento de stream como una primitiva de infraestructura separada.
  • Change Data Capture — Existe una pequeña industria alrededor de obtener datos de bases de datos, y este es el estilo de extracción de datos más amigable con los logs.
  • Enterprise Application Integration parece tratar sobre resolver el problema de integración de datos cuando lo que tienes es una colección de software empresarial de paquete como CRM o software de gestión de cadena de suministro.
  • Complex Event Processing (CEP): Estoy bastante seguro de que nadie sabe lo que esto significa o cómo difiere realmente del procesamiento de stream. La diferencia parece ser que el foco está en streams no ordenados y en el filtrado y detección de eventos en lugar de en la agregación, pero esto, en mi opinión, es una distinción sin diferencia. Creo que cualquier sistema que sea bueno en uno debería ser bueno en otro.
  • Enterprise Service Bus — Creo que el concepto de bus de servicio empresarial es muy similar a algunas de las ideas que he descrito en torno a la integración de datos. Esta idea parece haber tenido un éxito moderado en las comunidades de software empresarial y es mayormente desconocida entre la gente de la web o la multitud de infraestructura de datos distribuidos.

Cosas interesantes de código abierto:

  • Kafka Es el proyecto de “log como servicio” que es la base para gran parte de este post.
  • Bookeeper y Hedwig comprenden otro “log como servicio” de código abierto. Parecen estar más enfocados en los internos de los sistemas de datos que en los datos de eventos.
  • Databus es un sistema que proporciona una superposición tipo log para tablas de bases de datos.
  • Akka es un framework de actor para Scala. Tiene un complemento, eventsourced, que proporciona persistencia y registro.
  • Samza es un marco de procesamiento de stream en el que estamos trabajando en LinkedIn. Utiliza muchas de las ideas en este artículo, así como la integración con Kafka como el log subyacente.
  • Storm es un popular marco de procesamiento de stream que se integra bien con Kafka.
  • Spark Streaming es un marco de procesamiento de stream que forma parte de Spark.
  • Summingbird es una capa sobre Storm o Hadoop que proporciona una abstracción de cálculo conveniente.

Trato de mantenerme al día en esta área, así que si sabes de algunas cosas que he omitido, házmelo saber.