{"id":14999,"date":"2023-04-11T11:22:47","date_gmt":"2023-04-11T11:22:47","guid":{"rendered":"https:\/\/beta.bluetab.net\/?p=14999"},"modified":"2023-04-11T11:22:47","modified_gmt":"2023-04-11T11:22:47","slug":"lakehouse-streaming-en-aws-con-apache-flink-y-hudi","status":"publish","type":"post","link":"https:\/\/beta.bluetab.net\/en\/2023\/04\/lakehouse-streaming-en-aws-con-apache-flink-y-hudi\/","title":{"rendered":"LakeHouse Streaming en AWS con Apache Flink y Hudi"},"content":{"rendered":"<figure><a href=\"https:\/\/www.linkedin.com\/in\/albertojaenrevuelta\/\" target=\"_blank\" rel=\"noopener\"><img decoding=\"async\" width=\"150\" height=\"150\" src=\"https:\/\/beta.bluetab.net\/wp-content\/uploads\/2022\/03\/cropped-Isotipo-Bluetab-150x150.png\" alt=\"\" loading=\"lazy\"><\/a><\/figure>\n<h4><a href=\"https:\/\/www.linkedin.com\/in\/albertojaenrevuelta\/\" target=\"_blank\" rel=\"noopener\">Alberto Jaen<\/a><\/h4>\n<p>AWS Cloud Engineer<\/p>\n<figure><a href=\"https:\/\/www.linkedin.com\/in\/alfonsojerezizquierdo\/\" target=\"_blank\" rel=\"noopener\"><img decoding=\"async\" width=\"150\" height=\"150\" src=\"https:\/\/beta.bluetab.net\/wp-content\/uploads\/2022\/03\/cropped-Isotipo-Bluetab-150x150.png\" alt=\"\" loading=\"lazy\"><\/a><\/figure>\n<h4><a href=\"https:\/\/www.linkedin.com\/in\/alfonsojerezizquierdo\/\" target=\"_blank\" rel=\"noopener\">Alfonso Jerez<\/a><\/h4>\n<p>AWS Cloud Engineer<\/p>\n<figure><a href=\"https:\/\/www.linkedin.com\/in\/adrianjimenezhernandez\/\" target=\"_blank\" rel=\"noopener\"><img decoding=\"async\" width=\"150\" height=\"150\" src=\"https:\/\/beta.bluetab.net\/wp-content\/uploads\/2022\/03\/cropped-Isotipo-Bluetab-150x150.png\" alt=\"\" loading=\"lazy\"><\/a><\/figure>\n<h4><a href=\"https:\/\/www.linkedin.com\/in\/adrianjimenezhernandez\/\" target=\"_blank\" rel=\"noopener\">Adri\u00e1n Jim\u00e9nez<\/a><\/h4>\n<p>AWS Cloud Engineer<\/p>\n<h2>Introducci\u00f3n<\/h2>\n<p>Cada d\u00eda la ingesta y procesamiento de streams de datos en <i>Near Real Time <\/i>(<i>NRT<\/i>) es m\u00e1s necesario. Los requisitos de negocio son cada vez m\u00e1s exigentes en cuanto a tiempos de procesamiento y la disponibilidad de los datos m\u00e1s recientes y este art\u00edculo pretende abordar esta cuesti\u00f3n.<\/p>\n<p>Utilizando la nube de AWS y con un enfoque <i>serverless<\/i> se desplegar\u00e1 en este art\u00edculo una aplicaci\u00f3n capaz de ingestar streams de datos y procesarlos en <i>NRT<\/i>, escribiendo su resultado en un <i>LakeHouse<\/i> de tal manera que se puedan realizar operaciones ACID (Atomicidad, Consistencia, Aislamiento y Durabilidad) sobre estos. Se desplegar\u00e1 una arquitectura en la que se ingestan datos con Locust, se procesan con Flink y finalmente se escriben en Hudi y JSON.<\/p>\n<p>Locust es un framework de Python que sirve para poder realizar <i>Load Testing<\/i> de una manera f\u00e1cil y escalable. Las ventajas que ofrece Locust son la capacidad de poder definir este comportamiento de los usuarios con un lenguaje de prop\u00f3sito general y su facilidad de escalado.<\/p>\n<p>Flink se ha convertido en un <i>framework<\/i> de referencia en el \u00e1mbito de procesamiento distribuido sobre streams de datos. Se caracteriza por su orientaci\u00f3n al procesamiento de streams (aunque tambi\u00e9n puede ejecutar procesos batch), su rapidez de procesamiento y su eficiencia en el uso de memoria. Hay otros frameworks populares en el sector, como Spark Streaming y Storm, en el apartado de arquitectura se discutir\u00e1 por qu\u00e9 en \u00faltima instancia Flink ha sido el elegido.<\/p>\n<p>Finalmente, Hudi es un formato de fichero transaccional que proporciona las habilidades propias de una base de datos y <i>DataWarehouse<\/i> al <i>Data Lake<\/i>. Hudi da la capacidad de dejar atr\u00e1s los conceptos de batching y sustituirlo con una perspectiva de procesamiento incremental. Como el resto de las tecnolog\u00edas usadas en este art\u00edculo, se describe en detalle m\u00e1s adelante.<\/p>\n<p>Todo el c\u00f3digo utilizado en este art\u00edculo, tanto <i>IaC<\/i> como de Python, puede visitarse en nuestro repositorio<a href=\"\/#referencias\">[1]<\/a> en Github.<\/p>\n<h2>En pr\u00f3ximos art\u00edculos<\/h2>\n<p>M\u00faltiples art\u00edculos utilizar\u00e1n este como base para hablar de los siguientes temas:<\/p>\n<ul>\n<li style=\"font-weight: 400;\" aria-level=\"1\">Comparativa en cuanto a eficiencia de procesamiento, escritura y lectura de ficheros y costes en JSON vs Hudi.<\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\">Comparativa de <i>MOR<\/i> vs <i>COW<\/i>, adem\u00e1s del consumo de estas tablas por los distintos tipos de queries (<i>Snapshot, Read Optimized, Incremental<\/i>).<\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\">Escalabilidad.<\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\">Otras formas de explotaci\u00f3n del dato, como pueden ser Redshift o Pinot.<\/li>\n<\/ul>\n<h2>Arquitectura<\/h2>\n<p>A continuaci\u00f3n se puede ver la arquitectura a alto nivel que se desplegar\u00e1:<\/p>\n<p><img decoding=\"async\" width=\"760\" height=\"264\" src=\"https:\/\/beta.bluetab.net\/wp-content\/uploads\/2023\/04\/flink1.png\" alt=\"\" loading=\"lazy\" srcset=\"https:\/\/beta.bluetab.net\/wp-content\/uploads\/2023\/04\/flink1.png 760w, https:\/\/beta.bluetab.net\/wp-content\/uploads\/2023\/04\/flink1-300x104.png 300w\" sizes=\"(max-width: 760px) 100vw, 760px\"><\/p>\n<p>Como se puede ver, se utiliza Locust como herramienta de <i>Load Testing<\/i> para enviar datos sint\u00e9ticos a nuestra aplicaci\u00f3n. Estos ser\u00e1n ingestados a trav\u00e9s de un Kinesis Stream aprovisionado en modo <i>On Demand<\/i>, de esta manera el stream escalar\u00e1 de manera autom\u00e1tica. La alternativa al modo <i>On Demand<\/i> es el modo <i>Provisioned<\/i>, donde debemos especificar el n\u00famero de shards (componente en los que se divide el stream), con el que queremos aprovisionar el stream. Las diferencias y particularidades de estos dos modos se explicar\u00e1n m\u00e1s en detalle en el apartado de Kinesis.<\/p>\n<p>Del stream de entrada leen las dos aplicaciones de Kinesis Analytics Flink. Como se mencion\u00f3 en el apartado de pr\u00f3ximos pasos, la raz\u00f3n de tener dos aplicaciones independientes escribiendo en Hudi y JSON respectivamente es para realizar una comparativa en pr\u00f3ximos art\u00edculos en cuanto a eficiencia. Finalmente los datos se alojar\u00e1n en S3, el servicio de almacenamiento de objetos de AWS.<\/p>\n<p>La particularidad que tiene la aplicaci\u00f3n de Kinesis Analytics Flink es que es <i>serverless<\/i>, es decir, abstrae al desarrollador de la complejidad de configurar y desplegar un cluster con Flink. A esta aplicaci\u00f3n se deben asignar unos <i>KPUs<\/i> o <i>Kinesis Processing Units<\/i> y un jar con la librer\u00eda de Flink y los conectores necesarios para poder desplegarla correctamente. Todos estos conceptos ser\u00e1n explicados en los siguientes apartados.<\/p>\n<p>La alternativa a esta perspectiva <i>serverless<\/i> con un servicio administrado en AWS es la administraci\u00f3n completa de la aplicaci\u00f3n por parte del desarrollador, pudiendo utilizar herramientas como Kubernetes o EKS (Kubernetes administrado en AWS) para poder desplegar en un cluster esta aplicaci\u00f3n Flink. Las ventajas de esta segunda alternativa ser\u00eda el poder configurar tanto el cluster (n\u00famero de nodos, memoria, CPU, disco duro, etc\u2026) como la aplicaci\u00f3n Flink (gesti\u00f3n de <i>disaster recovery<\/i>, gesti\u00f3n de metadatos, etc\u2026) con un grado de detalle mucho mayor. En este art\u00edculo se decidi\u00f3 la primera alternativa por su simplicidad y facilidad de uso a la hora de conocer el <i>framework<\/i> de Flink.<\/p>\n<h2>Locust<\/h2>\n<p>La primera pieza en la pipeline de ingesta de datos es el componente de Locust escrito en Python. A diferencia de otros <i>frameworks<\/i> disponibles en el mercado como JMeter, Locust nos da la capacidad de poder escribir un c\u00f3digo simple con Python en vez de utilizar un lenguaje espec\u00edfico a un dominio o una interfaz de usuario.<\/p>\n<p>Adem\u00e1s, Locust est\u00e1 basado en eventos y utiliza greenlet<a href=\"\/#referencias\">[2]<\/a>, lo que le da la capacidad de con un solo hilo del procesador poder administrar la capacidad de varios miles de usuarios. Por ejemplo, en el caso de JMeter, se necesita un hilo para cada usuario, lo que supone un problema de escalabilidad para casos en los que se necesite un n\u00famero alto de estos.<\/p>\n<p>Locust tiene varias posibilidades a la hora de ejecutarse y escalar, pudiendo funcionar en local para aplicaciones con menos exigencias en cuanto a volumen de datos o desplegar en un cluster de Kubernetes al crear una imagen de Docker a ra\u00edz del c\u00f3digo de Locust.<\/p>\n<p>En cuanto a clientes y sistemas a los que enviar datos, Locust proporciona un cliente HTTP integrado. En el caso de querer enviar eventos a otros sistemas, como el de este art\u00edculo, siempre se puede escribir un cliente personalizado gracias a la ventaja de ser un <i>framework<\/i> de Python.<\/p>\n<p>Adem\u00e1s, Locust tambi\u00e9n proporciona una interfaz web para poder comprobar el progreso de tu env\u00edo de datos en tiempo real. Por todas estas razones se ha decidido utilizar esta tecnolog\u00eda en este art\u00edculo.<\/p>\n<h2>Kinesis Data Analytics<\/h2>\n<p>Para la ingesta de datos, se utilizar\u00e1 Kinesis Data Streams, un servicio de streaming de datos completamente administrado y serverless ofrecido por AWS. Un Kinesis Stream est\u00e1 formado por una agrupaci\u00f3n l\u00f3gica de <i>shards<\/i>, que representan la unidad fundamental de capacidad de un stream y son procesados en paralelo. Cada shard dota al stream de 1 MB\/s o 1,000 eventos por segundo de escritura y 2 MB\/s de lectura. Los eventos ser\u00e1n distribuidos entre los shards de un stream en funci\u00f3n de su clave de partici\u00f3n, por lo que es importante que el particionado sea homog\u00e9neo para evitar un sesgo en la distribuci\u00f3n y ocurrencia de <i>hot shards<\/i>. Existen dos modos de aprovisionamiento de capacidad:<\/p>\n<ul>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><i>On Demand<\/i> &#8211; el n\u00famero de shards se gestiona autom\u00e1ticamente para acomodar la carga, asegurando un rendimiento \u00f3ptimo sin necesidad de ajustes manuales.<\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><i>Provisioned<\/i> &#8211; debes especificar el n\u00famero de shards para el stream en funci\u00f3n de la carga esperada.<\/li>\n<\/ul>\n<p>Por simplicidad, y por ser id\u00f3neo para nuestro caso de uso, se optar\u00e1 por el modo <i>On Demand<\/i>. Esto acomodar\u00e1 autom\u00e1ticamente el n\u00famero de <i>shards<\/i> a la cantidad de datos generados por nuestra aplicaci\u00f3n de Locust.<\/p>\n<p>Para leer y procesar los datos ingestados a trav\u00e9s de Kinesis Data Streams, se usar\u00e1 otro servicio de la familia Kinesis, Kinesis Data Analytics (KDA). Este servicio es ofrecido en dos sabores<\/p>\n<ul>\n<li style=\"font-weight: 400;\" aria-level=\"1\">Kinesis Analytics SQL &#8211; Permite la creaci\u00f3n de aplicaciones de procesamiento de datos en streaming mediante el uso de SQL. Este servicio se considera deprecado en favor del servicio de KDA for Apache Flink.<\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\">Kinesis Analytics for Apache Flink &#8211; Proporciona una forma de desplegar un cluster de Flink gestionado por AWS. El uso de Flink faculta la creaci\u00f3n de aplicaciones m\u00e1s avanzadas y con mayor rendimiento.<\/li>\n<\/ul>\n<p>Una aplicaci\u00f3n de Flink consta de una serie de tareas de procesado en paralelo, tambi\u00e9n conocidas como operadores, que se conectan en una <i>Directed Acyclic Graph<\/i> (<i>DAG<\/i>). El stream de datos es procesado por esta <i>DAG<\/i>, con cada operador ejecutando una operaci\u00f3n espec\u00edfica sobre el dato.<\/p>\n<p>KDA asigna potencia de computaci\u00f3n para nuestra aplicaci\u00f3n en forma de Kinesis Processing (<i>KPUs<\/i>), cada una de ellas equivalente a 1 vCPU y 4GB de RAM. Se determina el n\u00famero de <i>KPUs<\/i> para la aplicaci\u00f3n mediante la especificaci\u00f3n de dos par\u00e1metros:<\/p>\n<ul>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><i>Parallelism<\/i> &#8211; N\u00famero de tareas que se pueden ejecutar concurrentemente.<\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><i>ParallelismPerKPU<\/i> &#8211; N\u00famero de tareas que pueden ejecutarse en una \u00fanica KPU.<\/li>\n<\/ul>\n<p>El n\u00famero total de <i>KPUs<\/i> de la aplicaci\u00f3n viene dado por <i>Parallelism <\/i>\/ <i>ParallelismPerKPU<\/i>. Es posible desplegar este servicio con autoescalado autom\u00e1tico, que ajustar\u00e1 autom\u00e1ticamente el n\u00famero de <i>KPUs<\/i> en funci\u00f3n del consumo de CPU para acomodar la demanda.<\/p>\n<figure>\n\t\t\t\t\t\t\t\t\t\t<img decoding=\"async\" width=\"343\" height=\"368\" src=\"https:\/\/beta.bluetab.net\/wp-content\/uploads\/2023\/04\/flink2.png\" alt=\"\" loading=\"lazy\" srcset=\"https:\/\/beta.bluetab.net\/wp-content\/uploads\/2023\/04\/flink2.png 343w, https:\/\/beta.bluetab.net\/wp-content\/uploads\/2023\/04\/flink2-280x300.png 280w\" sizes=\"(max-width: 343px) 100vw, 343px\"><figcaption>Figure 1. KDA configuration with Parallelism 4 and ParallelismPerKPU 2<\/figcaption><\/figure>\n<p>Los costos<a href=\"\/#referencias\">[3]<\/a> de Amazon Kinesis Analytics se basan en un modelo pay-per-use, apoy\u00e1ndose en las <i>Kinesis Processing Units<\/i> consumidas. Adem\u00e1s, se asume un coste por el almacenamiento usado por la aplicaci\u00f3n y sus copias de seguridad.<\/p>\n<h2>Flink<\/h2>\n<p>Profundizando m\u00e1s en la aplicaci\u00f3n de Flink, una de las caracter\u00edsticas m\u00e1s importantes es la capacidad de ser resiliente a fallos. Para ello, Flink incorpora un sistema de <i>checkpointing<\/i> mediante el cual se toma un <i>snapshot<\/i> de la aplicaci\u00f3n y su estado que es guardado en un almacenamiento remoto en caso de que sea necesario recuperar la aplicaci\u00f3n.<\/p>\n<p>El proceso de <i>checkpointing<\/i> de una aplicaci\u00f3n de Flink est\u00e1 dise\u00f1ado para ser resiliente y eficiente. Flink puede hacer uso de diferentes <i>backends<\/i> para guardar el estado de la aplicaci\u00f3n. El m\u00e1s simple ser\u00eda la memor\u00eda de la propia <i>Java Virtual Machine<\/i>, y aunque esto ofrece baja latencia y una gesti\u00f3n m\u00e1s simple, r\u00e1pidamente pueden surgir problemas de escalado y capacidad que no lo hacen recomendable para entornos de producci\u00f3n. Por eso es com\u00fan el uso de RocksDB como <i>backend<\/i> de Flink, una base de datos de clave-valor con alto rendimiento, escalable y con tolerancia a fallos. Adicionalmente KDA guarda estos <i>snapshots<\/i> en S3 para una capa extra de durabilidad.<\/p>\n<p>Para el prop\u00f3sito de este blog, se ha desarrollado una sencilla aplicaci\u00f3n de&nbsp; ingesta de datos en tiempo real y su posterior guardado en S3. Flink ofrece dos APIs mediante las cuales puedes desarrollar una aplicaci\u00f3n:<\/p>\n<ul>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><i>DataStream API<\/i> &#8211; Es una API basada en el concepto de streams. Ofrece control a bajo nivel de la aplicaci\u00f3n con la desventaja de requerir un mayor esfuerzo por parte del desarrollador.<\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><i>Table API<\/i> &#8211; Esta API se basa en el concepto de tablas. Ofrece una manera declarativa de desarrollar la aplicaci\u00f3n mediante el uso de expresiones SQL. Conlleva una p\u00e9rdida de control sobre los detalles de la aplicaci\u00f3n en favor de ser mucho m\u00e1s sencilla.<\/li>\n<\/ul>\n<p>Para este caso de uso se usar\u00e1 la <i>Table API<\/i> por su simplicidad, pero es igualmente compatible con el uso de la <i>DataStream API<\/i>.<\/p>\n<p>A la hora de desplegar la aplicaci\u00f3n con Kinesis Data Analytics s\u00f3lo es necesario definir el punto de entrada del c\u00f3digo de la aplicaci\u00f3n y proporcionar un <i>uber jar <\/i>con todas las dependencias de esta. Conviene explicar las dependencias usadas para esta aplicaci\u00f3n, pues suele ser uno de los mayores puntos de fricci\u00f3n a la hora desarrollar una aplicaci\u00f3n de Flink:<\/p>\n<ul>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><i>SQL connector for Kinesi<\/i>s &#8211; Conector fundamental para que nuestra aplicaci\u00f3n de Flink sea capaz de leer de un Kinesis Stream.<\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><i>S3 Filesystem for Hadoop<\/i> &#8211; Permite a la aplicaci\u00f3n operar sobre S3.<\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><i>Hudi Bundle<\/i> &#8211; Paquete proporcionado por los desarrolladores de Hudi, con todas las dependencias necesarias para trabajar con la tecnolog\u00eda.<\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><i>Hadoop MapReduce Client Core<\/i> &#8211; Dependencia adicional necesaria para que la escritura a Hudi funcione correctamente en KDA. Es posible que en futuras versiones del <i>Hudi Bundle<\/i> esta dependencia no sea necesaria.<\/li>\n<\/ul>\n<p>&nbsp;La aplicaci\u00f3n est\u00e1 preparada para escribir datos tanto en formato JSON como en tablas de Hudi <i>MoR<\/i> o <i>CoW<\/i> (que se explicar\u00e1n en detalle en la siguiente secci\u00f3n). Tanto el c\u00f3digo de la aplicaci\u00f3n como la infraestructura est\u00e1n disponibles en el repositorio.<\/p>\n<h2>Hudi<\/h2>\n<h4><strong>Conceptos<\/strong><\/h4>\n<p>Hudi se presenta como una fuente de almacenamiento Open Source a nivel de formato de datos. Al igual que hacen otras soluciones como Iceberg o Delta Lake, ofrece algunas propiedades ya existentes en estas como es el soporte de transacciones ACID (Atomicidad, Consistencia, Aislamiento y Durabilidad), procesos enfocados a la optimizaci\u00f3n de tareas de lectura\/escritura, actualizaci\u00f3n de datos incrementales y otras que se explicar\u00e1n a continuaci\u00f3n. Es importante resaltar que estas no podr\u00edan conseguirse mediante ficheros de formato Avro y Parquet.<\/p>\n<p>Las caracter\u00edsticas que presenta Hudi son las siguientes:<\/p>\n<ul>\n<li style=\"font-weight: 400;\" aria-level=\"1\">Transacciones ACID: unas de las principales ventajas que ofrece Apache Hudi es el soporte para transacciones ACID, posibilitando que las operaciones de escritura sean at\u00f3micas y consistentes. Adem\u00e1s tambi\u00e9n proporciona que los datos est\u00e9n aislados y sean duraderos, lo que garantiza la integridad de los datos y la consistencia del sistema. M\u00e1s adelante se analizar\u00e1 m\u00e1s en detalle c\u00f3mo las distintas formas de almacenamiento lo hacen posible y las ventajas que estas ofrecen.<\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><i>Pipelines<\/i> Incrementales<i>: <\/i>la clusterizaci\u00f3n de los eventos en funci\u00f3n de variables de negocio permite que tareas de borrado\/actualizaci\u00f3n de datos se puedan realizar de una forma m\u00e1s eficiente si estas se encuentran indexadas de forma conjunta aunque no se hayan dado en la misma franja temporal.<\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\">Ingesta en Streaming: Hudi permite obtener unos <i>workloads <\/i>computacionalmente menos pesados a trav\u00e9s de <i>Upserts<\/i> que recurren a una indentaci\u00f3n optimizada<a href=\"\/#referencias\">[4]<\/a> por grupos de archivos, lo que hace que en tareas de escritura (<i>Update<\/i>\/<i>Append<\/i>\/<i>Delete<\/i>) sean m\u00e1s eficientes. Esto permite que muchas de las aplicaciones basadas en Hudi no deban ser deduplicadas.<\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><i>Queries<\/i> de estados previos de los datos &#8211; <i>Time Travel<\/i>: Hudi permite actualizar y consultar informaci\u00f3n de particiones pasadas sin la necesidad de tener que reprocesar ni incluir particiones temporales mayores. De esta manera se asegura que eventos enviados con posterioridad no sean procesados y sean correctamente almacenados.<\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\">Tareas de escritura simult\u00e1neas: mediante <i>OCC<\/i> (<i>Optimistic Concurrency Control<\/i><a href=\"\/#referencias\">[5]<\/a>) se permite que muchas de las tareas como <i>Upsert<\/i> e <i>Insert<\/i> puedan realizarse correctamente aun realiz\u00e1ndose de forma simult\u00e1nea.<\/li>\n<\/ul>\n<p>A la hora de analizar c\u00f3mo Hudi procede a realizar el almacenamiento de los eventos ingestados, estos son agrupados por particiones y estas a su vez agrupadas en grupos de archivos. Estos \u00faltimos teniendo asignado un <i>file_id<\/i> \u00fanico para cada grupo en el cual se encuentra el <i>base file<\/i>, en formato parquet, el cual surge tras una acci\u00f3n, ya sea un <i>commit<\/i> o&nbsp; compactaci\u00f3n, y el log file que es donde se encuentran registrados todas las actualizaciones realizadas (<i>event version tracking<\/i>).<\/p>\n<h2>Tipos de Tablas y Queries<\/h2>\n<p>Hudi ofrece 2 tipos de tablas en funci\u00f3n de la necesidad de negocio, esto tiene un impacto a nivel de <i>performance<\/i> y limitaci\u00f3n de ciertas funcionalidades como se ver\u00e1n en m\u00e1s detalle:<\/p>\n<h4><strong><i>Copy on Write (COW)<\/i><\/strong><\/h4>\n<p>Sistema de almacenamiento mediante el cual en las tareas de actualizaci\u00f3n, eliminaci\u00f3n o registro de nuevos datos se realizan directamente sobre el archivo de logs (<i>delta file<\/i>) y se crea una nueva instant\u00e1nea que incluye una copia completa del conjunto de datos actualizado, incluyendo una nueva versi\u00f3n del base file y un archivo delta que contiene los cambios realizados en esa operaci\u00f3n.<\/p>\n<p>No es hasta la compactaci\u00f3n de datos (programada o al alcanzar un tama\u00f1o de datos definido) cuando se realiza la combinaci\u00f3n de los archivos delta con la versi\u00f3n m\u00e1s reciente del conjunto de datos completo.Se crea as\u00ed un nuevo archivo completo donde se eliminan los archivos delta que ya no son necesarios, actualizando a su vez el archivo de \u00edndice para que pueda acceder a los datos del archivo compactado.<\/p>\n<p>Este sistema de almacenamiento est\u00e1 especialmente recomendado para casos de uso en los que las tareas de lectura sean m\u00e1s frecuentes que las de escritura al no requerir de&nbsp; transformaciones de datos adicionales al leer los datos. A continuaci\u00f3n se muestra el <i>Timeline<\/i> de los principales archivos al realizarse las distintas tareas de escritura:<\/p>\n<table id=\"eael-data-table-f72646b\">\n<thead>\n<tr>\n<th id=\"\" colspan=\"\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tAcci\u00f3n<\/th>\n<th id=\"\" colspan=\"\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tNUEVO archivo base<\/th>\n<th id=\"\" colspan=\"\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tArchivo delta<\/th>\n<th id=\"\" colspan=\"\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tArchivo de \u00edndice<\/th>\n<th id=\"\" colspan=\"\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tSnapshot<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td colspan=\"\" rowspan=\"\" id=\"\">\n\t\t\t\t\t\t\t\t\t\t\t\tNuevo registro<\/td>\n<td colspan=\"\" rowspan=\"\" id=\"\">\n\t\t\t\t\t\t\t\t\t\t\t\tSe escribe el registro en el archivo base<\/td>\n<td colspan=\"\" rowspan=\"\" id=\"\">\n\t\t\t\t\t\t\t\t\t\t\t\tNo se crea un archivo delta<\/td>\n<td colspan=\"\" rowspan=\"\" id=\"\">\n\t\t\t\t\t\t\t\t\t\t\t\tSe actualiza el archivo de \u00edndice con el nuevo registro<\/td>\n<td colspan=\"\" rowspan=\"\" id=\"\">\n\t\t\t\t\t\t\t\t\t\t\t\tNo se crea un nuevo snapshot<\/td>\n<\/tr>\n<tr>\n<td colspan=\"\" rowspan=\"\" id=\"\">\n\t\t\t\t\t\t\t\t\t\t\t\tActualizaci\u00f3n de registro existente<\/td>\n<td colspan=\"\" rowspan=\"\" id=\"\">\n\t\t\t\t\t\t\t\t\t\t\t\tSe escribe el registro actualizando en un nuevo archivo base<\/td>\n<td colspan=\"\" rowspan=\"\" id=\"\">\n\t\t\t\t\t\t\t\t\t\t\t\tSe escribe el registro actualizando en el archivo delta<\/td>\n<td colspan=\"\" rowspan=\"\" id=\"\">\n\t\t\t\t\t\t\t\t\t\t\t\tSe actualiza el archivo de \u00edndice con la versi\u00f3n actualizada del registro<\/td>\n<td colspan=\"\" rowspan=\"\" id=\"\">\n\t\t\t\t\t\t\t\t\t\t\t\tNo se crea un nuevo snapshot<\/td>\n<\/tr>\n<tr>\n<td colspan=\"\" rowspan=\"\" id=\"\">\n\t\t\t\t\t\t\t\t\t\t\t\tEliminaci\u00f3n de registro<\/td>\n<td colspan=\"\" rowspan=\"\" id=\"\">\n\t\t\t\t\t\t\t\t\t\t\t\tNo se escribe el registro eliminado en el nuevo archivo<\/td>\n<td colspan=\"\" rowspan=\"\" id=\"\">\n\t\t\t\t\t\t\t\t\t\t\t\tSe escribe una marca de eliminaci\u00f3n en un nuevo archivo delta<\/td>\n<td colspan=\"\" rowspan=\"\" id=\"\">\n\t\t\t\t\t\t\t\t\t\t\t\tSe actualiza el archivo de \u00edndice con la marca de eliminaci\u00f3n<\/td>\n<td colspan=\"\" rowspan=\"\" id=\"\">\n\t\t\t\t\t\t\t\t\t\t\t\tNo se crea un nuevo snapshot<\/td>\n<\/tr>\n<tr>\n<td colspan=\"\" rowspan=\"\" id=\"\">\n\t\t\t\t\t\t\t\t\t\t\t\tCompactaci\u00f3n de archivos delta<\/td>\n<td colspan=\"\" rowspan=\"\" id=\"\">\n\t\t\t\t\t\t\t\t\t\t\t\tSe fusionan los archivos delta en un nuevo archivo base<\/td>\n<td colspan=\"\" rowspan=\"\" id=\"\">\n\t\t\t\t\t\t\t\t\t\t\t\tNo se crea un nuevo archivo delta<\/td>\n<td colspan=\"\" rowspan=\"\" id=\"\">\n\t\t\t\t\t\t\t\t\t\t\t\tSe crea un nuevo archivo \u00edndice que contiene todas las entradas del \u00edndice de los archivos fusionados<\/td>\n<td colspan=\"\" rowspan=\"\" id=\"\">\n\t\t\t\t\t\t\t\t\t\t\t\tSe crea un nuevo snapshot que refleja el estado actual de los datos despu\u00e9s de la compactaci\u00f3n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h4><strong><i>Merge On-Read (MOR)<\/i><\/strong><\/h4>\n<p>En este caso, no se utilizan <i>delta files<\/i> separados como en el modelo <i>Copy-on-Write (COW)<\/i>. En su lugar, los cambios se escriben directamente en los archivos de datos existentes (<i>base files<\/i>). En las tareas en las que se realizan actualizaciones de registros, estos nuevos son a\u00f1adidos en el <i>base file<\/i>, y en el caso de eliminaci\u00f3n, estos son marcados como tal en el base file, en ambos casos estos cambios son registrados en el archivo de \u00edndice, hasta que se realiza la compactaci\u00f3n. Es en esta operaci\u00f3n donde se aplican todas las actualizaciones a los registros en el archivo base correspondiente y elimina las versiones anteriores de los registros actualizados.&nbsp;<\/p>\n<p>Esta alternativa est\u00e1 especializada en realizar consultas de datos hist\u00f3ricos versionados y transformaciones y an\u00e1lisis <i>NRT<\/i> de grandes vol\u00famenes, ya que es posible realizarlo sin tener que copiar los datos a otra ubicaci\u00f3n en el disco. Adem\u00e1s de ser \u00f3ptimo para casos de uso en los que las tareas de escritura son concurrentes al ser m\u00e1s eficiente ya que no es necesario realizar transformaciones de datos adicionales durante la escritura, aunque posee una menor tolerancia al fallo ya que en caso de que el archivo de logs se corrompa puede generar p\u00e9rdida de las versiones de los datos.<\/p>\n<p>A continuaci\u00f3n se muestra el <i>Timeline<\/i> de los principales archivos al realizarse las distintas tareas de escritura:<\/p>\n<table id=\"eael-data-table-76242a8\">\n<thead>\n<tr>\n<th id=\"\" colspan=\"\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tAcci\u00f3n<\/th>\n<th id=\"\" colspan=\"\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tArchivo base<\/th>\n<th id=\"\" colspan=\"\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tArchivo delta<\/th>\n<th id=\"\" colspan=\"\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tArchivo de \u00edndice<\/th>\n<th id=\"\" colspan=\"\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tSnapshot<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td colspan=\"\" rowspan=\"\" id=\"\">\n\t\t\t\t\t\t\t\t\t\t\t\tNuevo registro<\/td>\n<td colspan=\"\" rowspan=\"\" id=\"\">\n\t\t\t\t\t\t\t\t\t\t\t\tSe escribe el registro en el archivo base<\/td>\n<td colspan=\"\" rowspan=\"\" id=\"\">\n\t\t\t\t\t\t\t\t\t\t\t\tNo se crea un archivo delta<\/td>\n<td colspan=\"\" rowspan=\"\" id=\"\">\n\t\t\t\t\t\t\t\t\t\t\t\tSe actualiza el archivo de \u00edndice con el nuevo registro<\/td>\n<td colspan=\"\" rowspan=\"\" id=\"\">\n\t\t\t\t\t\t\t\t\t\t\t\tNo se crea un nuevo snapshot<\/td>\n<\/tr>\n<tr>\n<td colspan=\"\" rowspan=\"\" id=\"\">\n\t\t\t\t\t\t\t\t\t\t\t\tActualizaci\u00f3n de registro existente<\/td>\n<td colspan=\"\" rowspan=\"\" id=\"\">\n\t\t\t\t\t\t\t\t\t\t\t\tSe escribe el registro actualizando en un nuevo archivo delta<\/td>\n<td colspan=\"\" rowspan=\"\" id=\"\">\n\t\t\t\t\t\t\t\t\t\t\t\tSe escribe el registro actualizando en el archivo delta correspondiente<\/td>\n<td colspan=\"\" rowspan=\"\" id=\"\">\n\t\t\t\t\t\t\t\t\t\t\t\tSe actualiza el archivo de \u00edndice con la versi\u00f3n actualizada del registro<\/td>\n<td colspan=\"\" rowspan=\"\" id=\"\">\n\t\t\t\t\t\t\t\t\t\t\t\tNo se crea un nuevo snapshot<\/td>\n<\/tr>\n<tr>\n<td colspan=\"\" rowspan=\"\" id=\"\">\n\t\t\t\t\t\t\t\t\t\t\t\tEliminaci\u00f3n de registro<\/td>\n<td colspan=\"\" rowspan=\"\" id=\"\">\n\t\t\t\t\t\t\t\t\t\t\t\tNo se elimina el registro del archivo base<\/td>\n<td colspan=\"\" rowspan=\"\" id=\"\">\n\t\t\t\t\t\t\t\t\t\t\t\tSe escribe una marca de eliminaci\u00f3n en un nuevo archivo delta<\/td>\n<td colspan=\"\" rowspan=\"\" id=\"\">\n\t\t\t\t\t\t\t\t\t\t\t\tSe actualiza el archivo de \u00edndice con la marca de eliminaci\u00f3n<\/td>\n<td colspan=\"\" rowspan=\"\" id=\"\">\n\t\t\t\t\t\t\t\t\t\t\t\tNo se crea un nuevo snapshot<\/td>\n<\/tr>\n<tr>\n<td colspan=\"\" rowspan=\"\" id=\"\">\n\t\t\t\t\t\t\t\t\t\t\t\tCompactaci\u00f3n de archivos delta<\/td>\n<td colspan=\"\" rowspan=\"\" id=\"\">\n\t\t\t\t\t\t\t\t\t\t\t\tSe fusionan los archivos delta en un nuevo archivo base<\/td>\n<td colspan=\"\" rowspan=\"\" id=\"\">\n\t\t\t\t\t\t\t\t\t\t\t\tSe crea un nuevo archivo delta que contiene las actualizaciones pendientes despu\u00e9s de la \u00faltima compactaci\u00f3n<\/td>\n<td colspan=\"\" rowspan=\"\" id=\"\">\n\t\t\t\t\t\t\t\t\t\t\t\tSe crea un nuevo archivo \u00edndice que contiene todas las entradas del \u00edndice de los archivos fusionados<\/td>\n<td colspan=\"\" rowspan=\"\" id=\"\">\n\t\t\t\t\t\t\t\t\t\t\t\tSe crea un nuevo snapshot que refleja el estado actual de los datos despu\u00e9s de la compactaci\u00f3n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Como resumen, se realiza una comparativa de las principales m\u00e9tricas de performance entre <em>Copy on-Write<\/em> y <em>Merge on-Read<\/em>:<\/p>\n<table id=\"eael-data-table-14d2eae\">\n<thead>\n<tr>\n<th id=\"\" colspan=\"\"><\/th>\n<th id=\"\" colspan=\"\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tCOW<\/th>\n<th id=\"\" colspan=\"\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tMOR<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td colspan=\"\" rowspan=\"\" id=\"\">\n\t\t\t\t\t\t\t\t\t\t\t\tCoste de escritura<\/td>\n<td colspan=\"\" rowspan=\"\" id=\"\">\n\t\t\t\t\t\t\t\t\t\t\t\tMayor<\/td>\n<td colspan=\"\" rowspan=\"\" id=\"\">\n\t\t\t\t\t\t\t\t\t\t\t\tMenor<\/td>\n<\/tr>\n<tr>\n<td colspan=\"\" rowspan=\"\" id=\"\">\n\t\t\t\t\t\t\t\t\t\t\t\tLatencia<\/td>\n<td colspan=\"\" rowspan=\"\" id=\"\">\n\t\t\t\t\t\t\t\t\t\t\t\tMayor<\/td>\n<td colspan=\"\" rowspan=\"\" id=\"\">\n\t\t\t\t\t\t\t\t\t\t\t\tMenor<\/td>\n<\/tr>\n<tr>\n<td colspan=\"\" rowspan=\"\" id=\"\">\n\t\t\t\t\t\t\t\t\t\t\t\tRendimiento de consulta<\/td>\n<td colspan=\"\" rowspan=\"\" id=\"\">\n\t\t\t\t\t\t\t\t\t\t\t\tMayor<\/td>\n<td colspan=\"\" rowspan=\"\" id=\"\">\n\t\t\t\t\t\t\t\t\t\t\t\tMenor antes de compactaci\u00f3n<br \/>\nIgual tras compactaci\u00f3n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<ul>\n<li style=\"font-weight: 400;\" aria-level=\"1\">Escritura: <i>COW<\/i> tiene un mayor costo de escritura que <i>MOR<\/i> debido a que cada vez que se realiza una operaci\u00f3n de escritura (ya sea a\u00f1adir un nuevo registro o actualizar uno existente), se crea un nuevo delta file y se deben actualizar los archivos de \u00edndice correspondientes. En cambio, en MOR, los registros se escriben directamente en el base file, lo que implica una menor cantidad de operaciones de escritura y, por lo tanto, un menor costo en t\u00e9rminos de rendimiento y uso de recursos.<\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\">Latencia: <i>COW<\/i> tiene un menor <i>data latency<\/i> que <i>MOR<\/i> debido a que los registros nuevos o actualizados se escriben primero en un delta file separado, en lugar de actualizar directamente el base file como en <i>MOR<\/i>.<\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\">Tiempos de consulta: <i>COW<\/i> tiene un menor tiempo de consulta que <i>MOR<\/i> debido a que en <i>COW<\/i>, los datos actualizados se almacenan en los <i>Delta Files<\/i> y los datos originales se mantienen en el <i>Base File<\/i>. Esto significa que no es necesario realizar ninguna operaci\u00f3n de lectura para obtener la versi\u00f3n actualizada de los datos.<\/li>\n<\/ul>\n<p>Hudi no solo ofrece distintas formas de almacenamiento, sino tambi\u00e9n, distintas formas de realizar consultas sobre la informaci\u00f3n almacenada, dependiendo de nuevo tanto de los casos de negocio como del tipo de almacenamiento escogido:<\/p>\n<ul>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><i>Snapshots<\/i>: consulta la \u00faltima versi\u00f3n procedente de un commit o compactaci\u00f3n. Gracias a este tipo de consultas, se pueden obtener las versiones de los datos en momentos espec\u00edficos gracias a la combinaci\u00f3n del <i>base<\/i> y <i>delta file<\/i> (<i>time travel<\/i>). Misma performance en <i>CoW<\/i> y <i>MoR<\/i>.<\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><i>Read Optimized<\/i>: \u00fanicamente disponible si el tipo de tabla en el que se almacenan los datos es <i>MoR<\/i>. Basado en la obtenci\u00f3n de vistas optimizadas para lectura de un conjunto de datos grande y distribuido. Esto se consigue mediante indexaci\u00f3n optimizada (<i>Bloom Filter Index<\/i>), lo que permite reducir considerablemente el tiempo de b\u00fasqueda de datos. Adem\u00e1s se apoya tambi\u00e9n en la compactaci\u00f3n de datos que hace que, de nuevo, las tareas de b\u00fasqueda sean menos costosas al disminuir el volumen de los mismos.<\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><i>Incremental<\/i>: Permite leer solo los datos actualizados o agregados desde la \u00faltima consulta. Esto ayuda a reducir el tiempo de lectura y el uso del almacenamiento en disco.<\/li>\n<\/ul>\n<h2>Conclusiones<\/h2>\n<p>En este art\u00edculo se ha descrito como desplegar una aplicaci\u00f3n que ingesta eventos en tiempo real y forma con la salida un <i>LakeHouse<\/i> con una arquitectura <i>serverless. <\/i>Con esto se ha buscado un nivel de abstracci\u00f3n intermedio de tal manera que sea una aplicaci\u00f3n simple pero con la suficiente potencia para poder llegar a utilizarse en entornos productivos reales.<\/p>\n<p>Desplegar aplicaciones basadas en la combinaci\u00f3n de tecnolog\u00edas como son Apache Flink y Hudi otorga la capacidad de procesar grandes vol\u00famenes de datos en tiempo real y de manera escalable. Esto combinado con la garant\u00eda que aportan las transacciones ACID, hace que la combinaci\u00f3n de Apache Flink y Apache Hudi sea una soluci\u00f3n s\u00f3lida para la ingesta y procesamiento de datos en entornos cr\u00edticos.<\/p>\n<p>A pesar de todas las ventajas que se han descrito cabe resaltar algunos inconvenientes que se han podido detectar desarrollando esta arquitectura. El mayor problema que se ha encontrado ha sido la resoluci\u00f3n de dependencias entre las librer\u00edas de Flink y los conectores necesarios, como por ejemplo el de Hudi. La falta de comunidad que existe a d\u00eda de hoy, aunque esta crecer\u00e1 con el paso del tiempo, supuso un problema inicial considerable para poder formar el paquete final con todas las dependencias necesarias sin que hubiese conflictos entre s\u00ed. Adem\u00e1s, cabe resaltar que se ha percibido menos comunidad para el lenguaje de Python que para el de Java o Scala. En este art\u00edculo se eligi\u00f3 Python ya que exist\u00eda un conocimiento interno m\u00e1s fuerte pero en el caso de que el stack tecnol\u00f3gico se acerque m\u00e1s a lenguajes soportados por la <i>JVM<\/i> (<i>Java Virtual Machine<\/i>) ser\u00eda aconsejable el uso de Scala o Java.<\/p>\n<p>En los pr\u00f3ximos art\u00edculos entraremos m\u00e1s en detalle en las particularidades que tienen tanto Hudi como Flink para poder personalizar y ajustar el comportamiento de esta aplicaci\u00f3n dependiendo de las necesidades que presente nuestro caso de uso.<\/p>\n<h2>Referencias<\/h2>\n<p>[1] Repositorio Github Flink-Hudi (Terraform). [<a href=\"https:\/\/github.com\/ajaen4\/kinesis-flink-hudi-benchmark\">link<\/a>]<\/p>\n<p>[2] Greenlet 2.0.2. Documentation [<a href=\"https:\/\/pypi.org\/project\/greenlet\/\">link<\/a>] (February 28, 2023)<\/p>\n<p>[3] Amazon Kinesis Data Analytics Costs. [<a href=\"https:\/\/aws.amazon.com\/es\/kinesis\/data-analytics\/pricing\/\">link<\/a>] (March 23, 2022)<\/p>\n<p>[4] Hudi Optimized Indexing. [<a href=\"https:\/\/hudi.apache.org\/docs\/indexing\/\">link<\/a>] (September 23, 2021)<\/p>\n<p>[5] Hudi Writing Concurrency. [<a href=\"https:\/\/hudi.apache.org\/docs\/concurrency_control\/#multi-writer-guarantees\">link<\/a>] (September 23, 2021)<\/p>\n<h2>Autores<\/h2>\n<figure><a href=\"https:\/\/www.linkedin.com\/in\/albertojaenrevuelta\/\" target=\"_blank\" rel=\"noopener\"><img decoding=\"async\" width=\"150\" height=\"150\" src=\"https:\/\/beta.bluetab.net\/wp-content\/uploads\/2022\/03\/cropped-Isotipo-Bluetab-150x150.png\" alt=\"\" loading=\"lazy\"><\/a><\/figure>\n<h4><a href=\"https:\/\/www.linkedin.com\/in\/albertojaenrevuelta\/\" target=\"_blank\" rel=\"noopener\">Alberto Jaen<\/a><\/h4>\n<p>AWS Cloud Engineer<\/p>\n<p>Empec\u00e9 mi carrera laboral con el desarrollo, mantenimiento y administraci\u00f3n de bases de datos multidimensionales y <em>Data Lakes<\/em>. A partir de ah\u00ed comenc\u00e9 a estar interesado en plataformas de datos y arquitecturas cloud, estando certificado 3 veces en AWS y 2 con Hashicorp.<\/p>\n<p>Actualmente me encuentro trabajando como un <em>Cloud Engineer <\/em>desarrollando Data Lakes y DataWarehouses con AWS para un cliente relacionado con la organizaci\u00f3n de eventos deportivos a nivel mundial.<\/p>\n<figure><a href=\"https:\/\/www.linkedin.com\/in\/alfonsojerezizquierdo\/\" target=\"_blank\" rel=\"noopener\"><img decoding=\"async\" width=\"150\" height=\"150\" src=\"https:\/\/beta.bluetab.net\/wp-content\/uploads\/2022\/03\/cropped-Isotipo-Bluetab-150x150.png\" alt=\"\" loading=\"lazy\"><\/a><\/figure>\n<h4><a href=\"https:\/\/www.linkedin.com\/in\/alfonsojerezizquierdo\/\" target=\"_blank\" rel=\"noopener\">Alfonso Jerez<\/a><\/h4>\n<p>AWS Cloud Engineer<\/p>\n<p>Comenc\u00e9 mi carrera como Data Scientist en distintos sectores (banca, consultor\u00eda,\u2026) enfocado en la automatizaci\u00f3n de procesos y desarrollo de modelos. En los \u00faltimos a\u00f1os apost\u00e9 por Bluetab motivado por el inter\u00e9s en especializarme como Data Engineer y comenzar a trabajar con los principales proveedores Cloud (AWS, GPC y Azure) en clientes como Olympics, espec\u00edficamente en la optimizaci\u00f3n del procesamiento y almacenamiento del dato.<\/p>\n<p>Colaborando activamente con el grupo de Pr\u00e1ctica Cloud en investigaciones y desarrollo de blogs de tecnolog\u00edas punteras e innovadoras tales como esta, fomentando as\u00ed el continuo aprendizaje.<\/p>\n<figure><a href=\"https:\/\/www.linkedin.com\/in\/adrianjimenezhernandez\/\" target=\"_blank\" rel=\"noopener\"><img decoding=\"async\" width=\"150\" height=\"150\" src=\"https:\/\/beta.bluetab.net\/wp-content\/uploads\/2022\/03\/cropped-Isotipo-Bluetab-150x150.png\" alt=\"\" loading=\"lazy\"><\/a><\/figure>\n<h4><a href=\"https:\/\/www.linkedin.com\/in\/adrianjimenezhernandez\/\" target=\"_blank\" rel=\"noopener\">Adri\u00e1n Jim\u00e9nez<\/a><\/h4>\n<p>AWS Cloud Engineer<\/p>\n<p>Dedicado al aprendizaje constante de nuevas tecnolog\u00edas y su aplicaci\u00f3n, disfrutando de utilizarlas en la resoluci\u00f3n de desaf\u00edos tecnol\u00f3gicos. Desarrollo mi carrera como Cloud Engineer dise\u00f1ando, implementando y manteniendo infraestructura en AWS.<\/p>\n<p>Colaboro activamente en la Pr\u00e1ctica Cloud, donde investigamos y experimentamos con nuevas tecnolog\u00edas, buscando soluciones para los retos que enfrentan nuestros clientes.<\/p>\n<h4>\n\t\t\t\tNavegaci\u00f3n<\/h4>\n<h5>\u00bfQuieres saber m\u00e1s de lo que ofrecemos y ver otros casos de \u00e9xito?<\/h5>\n<p><a href=\"\/\" role=\"button\"><br \/>\nDESCUBRE BLUETAB<br \/>\n<\/a><\/p>\n<p><b>SOLUCIONES, <\/b>SOMOS EXPERTOS<\/p>\n<p><a href=\"\/soluciones\/data-strategy\/\"><\/a><\/p>\n<p><a href=\"\/soluciones\/data-strategy\/\"><\/p>\n<h5>DATA STRATEGY<\/h5>\n<p><\/a><a href=\"\/soluciones\/data-strategy\/\">\t\t\t\t\t\t<\/a><br \/>\n<a href=\"\/soluciones\/data-fabric\/\"><\/a><\/p>\n<p><a href=\"\/soluciones\/data-fabric\/\"><\/p>\n<h5>DATA FABRIC<\/h5>\n<p><\/a><a href=\"\/soluciones\/data-fabric\/\">\t\t\t\t\t\t<\/a><br \/>\n<a href=\"\/soluciones\/augmented-analytics\/\"><\/a><\/p>\n<p><a href=\"\/soluciones\/augmented-analytics\/\"><\/p>\n<h5>AUGMENTED ANALYTICS<\/h5>\n<p><\/a><a href=\"\/soluciones\/augmented-analytics\/\">\t\t\t\t\t\t<\/a><\/p>\n<p>Te puede interesar<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Alberto Jaen AWS Cloud Engineer Alfonso Jerez AWS Cloud Engineer Adri\u00e1n Jim\u00e9nez AWS Cloud Engineer Introducci\u00f3n Cada d\u00eda la ingesta y procesamiento de streams de datos en Near Real Time (NRT) es m\u00e1s necesario. Los requisitos de negocio son cada vez m\u00e1s exigentes en cuanto a tiempos de procesamiento y la disponibilidad de los datos [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":17831,"comment_status":"closed","ping_status":"open","sticky":false,"template":"elementor_header_footer","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[7,8,9],"tags":[],"class_list":["post-14999","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-blog-es","category-practices","category-tech"],"acf":[],"jetpack_featured_media_url":"https:\/\/beta.bluetab.net\/wp-content\/uploads\/2023\/04\/6.png","_links":{"self":[{"href":"https:\/\/beta.bluetab.net\/en\/wp-json\/wp\/v2\/posts\/14999","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/beta.bluetab.net\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/beta.bluetab.net\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/beta.bluetab.net\/en\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/beta.bluetab.net\/en\/wp-json\/wp\/v2\/comments?post=14999"}],"version-history":[{"count":0,"href":"https:\/\/beta.bluetab.net\/en\/wp-json\/wp\/v2\/posts\/14999\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/beta.bluetab.net\/en\/wp-json\/wp\/v2\/media\/17831"}],"wp:attachment":[{"href":"https:\/\/beta.bluetab.net\/en\/wp-json\/wp\/v2\/media?parent=14999"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/beta.bluetab.net\/en\/wp-json\/wp\/v2\/categories?post=14999"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/beta.bluetab.net\/en\/wp-json\/wp\/v2\/tags?post=14999"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}