<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Alvaro Garcia</title>
    <description>The latest articles on DEV Community by Alvaro Garcia (@alvarongg).</description>
    <link>https://dev.to/alvarongg</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1112823%2F3f85d929-b7b8-41a1-ad50-1bfca35854f8.jpeg</url>
      <title>DEV Community: Alvaro Garcia</title>
      <link>https://dev.to/alvarongg</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/alvarongg"/>
    <language>en</language>
    <item>
      <title>Cómo armé un Pit Wall con AWS IoT Core (y por qué este patrón sirve para cualquier industria)</title>
      <dc:creator>Alvaro Garcia</dc:creator>
      <pubDate>Sat, 12 Sep 2026 03:45:43 +0000</pubDate>
      <link>https://dev.to/alvarongg/como-arme-un-pit-wall-con-aws-iot-core-y-por-que-este-patron-sirve-para-cualquier-industria-4lo1</link>
      <guid>https://dev.to/alvarongg/como-arme-un-pit-wall-con-aws-iot-core-y-por-que-este-patron-sirve-para-cualquier-industria-4lo1</guid>
      <description>&lt;p&gt;Hola a todos! Hace unos meses me surgio la idea de empezar a correr en simrace nuevamente, en pandemia me compré el volante y lo usé un rato pero perdi la motivación cuando me di cuenta de lo dificil que es manejar deportivamente y mucho más dificil es mejorar sin tener un experto que te de indicaciones claras sobre que estás haciendo mal, por eso durante un tiempo investigue bastante y vi que si queria mejorar necesitaba si o si un coach, pero como en cloudhesive tengo una cuenta de aws para poder investigar me puse a la tarea de crear un sistema que me ayude a mejorar mis tiempos de vuelta y me puse a armar un pit wall digital para sim racing y mostrar el resultado en el AWS Community Day Argentina, el 12 de septiembre de 2026.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⭐ Todo el material de mis charlas — slides, código, diagramas — vive en &lt;a href="https://github.com/alvarongg/charlas-pub" rel="noopener noreferrer"&gt;github.com/alvarongg/charlas-pub&lt;/a&gt;. Si le tirás una estrella al repo te va a llegar una notificación cada vez que suba contenido nuevo. No spam, solo técnica.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  La idea
&lt;/h2&gt;

&lt;p&gt;La idea es simple — quiero mejorar como piloto, y para eso necesito ver mi telemetría en vivo, tener un historial de mis vueltas, y que un experto me diga en qué me estoy equivocando (y en donde). Para esta primera etapa elegi Assetto Corsa (el simulador) ya que tiene una funcionalidad que se llama "Shared Memory" la genera toda esa información en tiempo real, pero se queda ahí, en la memoria compartida de mi PC. Nadie más la ve, y cuando cierro el juego, se pierde.&lt;/p&gt;

&lt;p&gt;Luego de un trabajo de investigación de un par de dias, ya tenia mi primera fase completada, logré leer la memoria del simulador de forma correcta, sin romper el juego y sin problemas de offset o padding. (Gracias a la comunidad de asseto por toda la ingenieria inversa que hicieron sobre el juego ya que la documentación es basatnte pobre en este sentido. &lt;/p&gt;

&lt;p&gt;Ahora ya tenia como capturar los datos, necesitaba una forma de enviarlos a aws para poder procesarlso, Así que terminé usando esto como excusa para meterme de lleno en AWS IoT Core. Y cuanto más avanzaba, más me daba cuenta de que el problema que estaba resolviendo no tiene nada que ver con autos: es el mismo problema que tiene cualquiera que necesite sacar datos de un dispositivo que &lt;strong&gt;no vive dentro de tu infraestructura&lt;/strong&gt; — un sensor en una planta, un camión de una flota, un wearable médico, una máquina en una línea de producción. &lt;/p&gt;

&lt;p&gt;En todos esos casos el desafío es idéntico: cómo hacés que ese dispositivo te mande datos de forma segura, sin que tenga que confiar ciegamente en tu backend, y cómo procesás eso en tiempo real sin que se te caiga todo con el primer pico de tráfico.&lt;/p&gt;

&lt;p&gt;Este post es sobre eso. Voy a usar el pit wall como caso de estudio porque es el que tengo andando, pero el hilo conductor es entender el patrón: &lt;strong&gt;capturar información de un dispositivo fuera de AWS, y procesarla con AWS IoT&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  El problema, en términos generales
&lt;/h2&gt;

&lt;p&gt;Antes de meterme en el proyecto, pensemos el problema sin autos de por medio. Tenés N dispositivos, en cualquier lado, generando datos todo el tiempo. Necesitás:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Que cada dispositivo pueda mandarte datos sin que vos tengas que confiar en él más de lo necesario — si se compromete uno, no querés que ese dispositivo pueda hacerse pasar por otro, ni leer los datos de los demás.&lt;/li&gt;
&lt;li&gt;Que la conexión no dependa de que tu backend esté "escuchando" activamente — el dispositivo tiene que poder mandar datos aunque tu Lambda esté fría, aunque tu API esté de mantenimiento, aunque haya 10.000 dispositivos mandando al mismo tiempo.&lt;/li&gt;
&lt;li&gt;Procesar esos datos apenas llegan, sin que cada dispositivo tenga que saber nada de tu arquitectura interna — el dispositivo publica, y listo. Lo que pase después (guardar en una base, avisar a alguien, calcular algo) es responsabilidad tuya, no suya.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Ese es, casi textual, el problema que resuelve &lt;strong&gt;AWS IoT Core&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Por qué no "le pego un POST a un API Gateway"
&lt;/h2&gt;

&lt;p&gt;La primera pregunta que me hice fue esa: ¿por qué no simplemente expongo un endpoint HTTP y que cada dispositivo me mande un POST? Funcionaría para pocos dispositivos, pero se rompe rápido en varios frentes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Autenticación por certificado, no por API key.&lt;/strong&gt; Cada dispositivo se identifica con un certificado X.509 propio. Si alguien saca la PC de un simulador del evento y le copia el certificado, ese certificado solo sirve para &lt;em&gt;ese&lt;/em&gt; simulador — nunca para publicar en nombre de otro. Con una API key compartida, perdés esa contención.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MQTT, no HTTP.&lt;/strong&gt; El protocolo mantiene una conexión persistente y liviana, pensada para dispositivos con recursos limitados y redes poco confiables — reconecta solo, con backoff, y no perdés el mensaje si la conexión se corta a mitad de camino.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;El "Rules Engine" desacopla al dispositivo de tu backend.&lt;/strong&gt; El dispositivo no sabe que existe una Lambda, ni una tabla, ni nada. Publica en un topic y se desentiende. Vos decidís, del lado de AWS, qué Lambda se invoca, con qué filtro, y podés cambiar esa lógica sin tocar una sola línea del dispositivo.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ese último punto es el que más me voló la cabeza cuando lo entendí: &lt;strong&gt;la lógica de "qué hacer con el dato" vive completamente separada de la lógica de "cómo llega el dato"&lt;/strong&gt;. Podés agregar un procesamiento nuevo sin redeployar nada en los dispositivos.&lt;/p&gt;

&lt;h2&gt;
  
  
  Identidad de dispositivo: mínimo privilegio, en serio
&lt;/h2&gt;

&lt;p&gt;Acá está la primera parte del código. Cada simulador (&lt;code&gt;pit_wall-sim-01&lt;/code&gt;, &lt;code&gt;02&lt;/code&gt;, &lt;code&gt;03&lt;/code&gt;, &lt;code&gt;04&lt;/code&gt;) tiene su propio &lt;em&gt;Thing&lt;/em&gt; en IoT Core, con su propia política. La política no es un checkbox de "puede publicar" — define &lt;strong&gt;exactamente&lt;/strong&gt; en qué topics puede publicar y a cuáles se puede suscribir:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;policy_document&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sim_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;topic_prefix&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sim&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;base&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;topic_prefix&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;sim_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Version&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2012-10-17&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Statement&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Sid&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Connect&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Effect&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Allow&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Action&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;iot:Connect&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Resource&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;arn:aws:iot:...:client/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;sim_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Sid&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;PublishOutbound&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Effect&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Allow&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Action&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;iot:Publish&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Resource&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                    &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;arn:aws:iot:...:topic/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;topic&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
                    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;topic&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;telemetry&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;event&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;lap&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;session&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;log&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Sid&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SubscribeCommandAsAgent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Effect&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Allow&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Action&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;iot:Subscribe&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;iot:Receive&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Resource&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;arn:aws:iot:...:topic/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/command&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fijate lo que &lt;strong&gt;no&lt;/strong&gt; puede hacer este certificado: no puede publicar en &lt;code&gt;sim/pit-wall-sim-02/telemetry&lt;/code&gt; (el topic de otro simulador), y no puede suscribirse a nada que no sea su propio topic de comandos. Si mañana alguien clona el certificado de un simulador, el radio de daño queda contenido a ese único dispositivo — nunca escala al resto de la flota.&lt;/p&gt;

&lt;p&gt;Esto es literalmente lo mismo que harías con un sensor de humedad en una planta agrícola, o con la tablet de un camión de reparto: un certificado por dispositivo, un permiso mínimo por dispositivo, sin excepciones.&lt;/p&gt;

&lt;h2&gt;
  
  
  IoT Rules: el corazón del procesamiento
&lt;/h2&gt;

&lt;p&gt;Una vez que el dato entra por MQTT, ¿quién lo agarra? Ahí entran las &lt;strong&gt;IoT Rules&lt;/strong&gt;: consultas SQL que corren sobre cada mensaje que pasa por un topic, y que disparan una o más acciones.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;DATA_TOPICS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;telemetry&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;event&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;lap&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;session&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_crear_regla&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;topic&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;regla&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;iot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CfnTopicRule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Rule&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;topic&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;topic_rule_payload&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;iot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CfnTopicRule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;TopicRulePayloadProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SELECT * FROM &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;topic_prefix&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/+/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;topic&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;actions&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="n"&gt;iot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CfnTopicRule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ActionProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="n"&gt;lambda_&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;iot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CfnTopicRule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;LambdaActionProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                        &lt;span class="n"&gt;function_arn&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ingest_function&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;function_arn&lt;/span&gt;
                    &lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="n"&gt;iot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CfnTopicRule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ActionProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="n"&gt;lambda_&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;iot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CfnTopicRule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;LambdaActionProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                        &lt;span class="n"&gt;function_arn&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;broadcast_function&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;function_arn&lt;/span&gt;
                    &lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F193ihwy5cjxhp55qzcl1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F193ihwy5cjxhp55qzcl1.png" alt=" " width="800" height="439"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Lo interesante acá no es el SQL (&lt;code&gt;SELECT * FROM 'sim/+/telemetry'&lt;/code&gt;, con el &lt;code&gt;+&lt;/code&gt; como wildcard de un nivel — cualquier simulador, ese topic puntual). Lo interesante es que &lt;strong&gt;una sola regla dispara dos acciones en paralelo&lt;/strong&gt;, sobre el mismo mensaje:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Una Lambda de &lt;strong&gt;ingesta&lt;/strong&gt;, que persiste el dato (más sobre esto abajo).&lt;/li&gt;
&lt;li&gt;Una Lambda de &lt;strong&gt;broadcast&lt;/strong&gt;, que reenvía el mismo mensaje por WebSocket a quien esté mirando ese simulador en vivo.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No hay una segunda suscripción al mismo topic, ni una cola intermedia, ni el ingesta reenviándole el dato al broadcast. Es la misma regla, la misma invocación de IoT Core, dos acciones independientes. Si mañana el broadcast se cae, la ingesta sigue funcionando — y viceversa.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Este es el patrón que más me sirvió entender de todo el proyecto: &lt;strong&gt;una IoT Rule no es "un trigger", es un punto de fan-out&lt;/strong&gt;. Podés colgar tantas acciones como necesites (otra Lambda, un tópico SNS, un stream de Kinesis, otra regla) sin tocar nada del lado del dispositivo ni de las reglas existentes.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  El desafío real: reconstruir algo coherente a partir de pedacitos
&lt;/h2&gt;

&lt;p&gt;Acá viene la parte menos obvia. MQTT tiene un límite de tamaño de payload, y la telemetría de un simulador genera muestras varias veces por segundo — no entra todo en un solo mensaje. El agente en la PC del simulador arma &lt;strong&gt;lotes&lt;/strong&gt; (batches) de muestras y los publica de a poco, con un número de secuencia cada uno.&lt;/p&gt;

&lt;p&gt;Del lado de AWS, eso significa que la Lambda de ingesta nunca recibe "una vuelta completa" — recibe fragmentos, que van a &lt;code&gt;staging&lt;/code&gt; en DynamoDB con un TTL (si una sesión nunca cierra una vuelta, ese staging se autodestruye solo). Cuando llega el mensaje que dice "esta vuelta se cerró", ahí sí, la Lambda junta todos los lotes que tiene en staging para esa sesión y reconstruye la traza completa:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CoberturaIncompletaError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ReconstructionError&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;La cobertura de lotes en staging no alcanza — todavía.

    Las IoT Rules invocan la Lambda una vez por mensaje, sin ninguna
    garantía de orden entre invocaciones concurrentes: el mensaje de
    &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;vuelta cerrada&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; puede llegar antes de que termine de guardarse
    su último lote de telemetría. Acá se reintenta unos segundos
    dentro de la misma invocación, en vez de descartar la vuelta.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Este tipo de problema — datos que llegan en desorden, sin garantía de que el último pedacito ya esté cuando lo necesitás — es &lt;em&gt;el&lt;/em&gt; problema clásico de cualquier pipeline de IoT en tiempo real. No es específico de telemetría de autos: es lo mismo que te pasa reconstruyendo el recorrido de un camión a partir de puntos GPS sueltos, o el estado de una máquina a partir de eventos de sensores que no llegan en orden.&lt;/p&gt;

&lt;h2&gt;
  
  
  Guardado: una tabla, un bucket, y ya
&lt;/h2&gt;

&lt;p&gt;Del lado del guardado no hay demasiada magia, pero vale la pena nombrarlo porque es donde termina el dato una vez procesado:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;DynamoDB&lt;/strong&gt;, en una tabla única (single-table design): sesiones, vueltas, recomendaciones, todo con &lt;code&gt;PK&lt;/code&gt;/&lt;code&gt;SK&lt;/code&gt; pensados para que cada consulta sea una &lt;code&gt;Query&lt;/code&gt; barata sobre un prefijo, nunca un &lt;code&gt;Scan&lt;/code&gt; de toda la tabla.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;S3&lt;/strong&gt;, para lo pesado: la telemetría reconstruida, los archivos de audio, cualquier blob que no tenga sentido meter en DynamoDB.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Nada de esto es exclusivo de IoT — es el mismo par de servicios que usarías para cualquier backend serverless. Lo importante es que &lt;strong&gt;para cuando el dato llega acá, ya pasó por todo el trabajo pesado de identidad, transporte y reconstrucción&lt;/strong&gt;, así que esta capa queda simple.&lt;/p&gt;

&lt;h2&gt;
  
  
  De IoT a "en vivo": el otro lado del fan-out
&lt;/h2&gt;

&lt;p&gt;Contaba arriba que la misma IoT Rule dispara dos Lambdas en paralelo. La segunda (&lt;code&gt;broadcast&lt;/code&gt;) es la que arma la parte "en vivo" del dashboard: mantiene un índice chico de qué conexión de WebSocket está mirando qué simulador, y cuando llega un mensaje nuevo, lo reenvía tal cual a esas conexiones.&lt;/p&gt;

&lt;p&gt;Fijate que este Lambda &lt;strong&gt;no procesa nada&lt;/strong&gt; — no interpreta, no clasifica, no llama a ningún otro servicio. Solo reenvía. Esa separación entre "el camino que persiste" y "el camino que muestra en vivo" es la que permite que uno pueda evolucionar sin romper al otro: si un día quiero agregar un análisis con IA arriba de la ingesta (spoiler: lo hice, es tema de otro post), el WebSocket en vivo ni se entera.&lt;/p&gt;

&lt;h2&gt;
  
  
  Generalizando el patrón
&lt;/h2&gt;

&lt;p&gt;Volvamos a la idea del principio: esto no es sobre autos. La misma arquitectura, con los mismos cuatro bloques (identidad por dispositivo → IoT Rules → procesamiento → guardado/consumo), sirve para:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Industria&lt;/th&gt;
&lt;th&gt;El "dispositivo"&lt;/th&gt;
&lt;th&gt;El dato&lt;/th&gt;
&lt;th&gt;Qué reemplaza al pit wall&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Agro&lt;/td&gt;
&lt;td&gt;Sensor de humedad/temperatura en el campo&lt;/td&gt;
&lt;td&gt;Lecturas periódicas&lt;/td&gt;
&lt;td&gt;Un dashboard de riego automatizado&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Logística&lt;/td&gt;
&lt;td&gt;Tablet o GPS de un camión de reparto&lt;/td&gt;
&lt;td&gt;Posición, velocidad, paradas&lt;/td&gt;
&lt;td&gt;Seguimiento de flota en vivo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Industria&lt;/td&gt;
&lt;td&gt;PLC o sensor de una línea de producción&lt;/td&gt;
&lt;td&gt;Vibración, temperatura, ciclos&lt;/td&gt;
&lt;td&gt;Mantenimiento predictivo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Salud&lt;/td&gt;
&lt;td&gt;Wearable de un paciente&lt;/td&gt;
&lt;td&gt;Frecuencia cardíaca, pasos&lt;/td&gt;
&lt;td&gt;Alertas en tiempo real al equipo médico&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;En todos los casos cambia el dominio, pero no cambia la pregunta que tenés que responder: &lt;em&gt;¿cómo identifico a cada dispositivo sin que uno pueda hacerse pasar por otro, y cómo proceso lo que manda sin acoplar mi lógica de negocio a los detalles del transporte?&lt;/em&gt; Esa pregunta la responde AWS IoT Core, la resuelvas con autos, camiones o sensores de humedad.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvyae3y1rsl9fithhggvj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvyae3y1rsl9fithhggvj.png" alt=" " width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Próximos pasos
&lt;/h2&gt;

&lt;p&gt;Terminé este proyecto sabiendo mucho más de IoT que de lo que sabía de simracing cuando arranqué, que ya es decir bastante. Todo lo de acá es la base: una vez que tenés el dato capturado, identificado y procesado, podés poner arriba lo que quieras. En mi caso fue un ingeniero de carrera que te habla por voz mientras manejás, generado con Amazon Bedrock y Amazon Polly — de eso hablo en el próximo post.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Una última cosa antes de cerrar:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Todo el material de esta charla — slides, código, diagramas — está en &lt;a href="https://github.com/alvarongg/charlas-pub" rel="noopener noreferrer"&gt;github.com/alvarongg/charlas-pub&lt;/a&gt;. Voy a estar subiendo contenido nuevo seguido: más posts como este, ejemplos de código ejecutable y arquitecturas de referencia.&lt;/p&gt;

&lt;p&gt;Si le tirás una ⭐ al repo GitHub te avisa automáticamente cuando haya novedades. Es la forma más fácil de no perderte nada — sin newsletter, sin formularios, sin nada raro.&lt;/p&gt;

&lt;p&gt;Abrazo grande. Hasta la próxima 🚀&lt;/p&gt;

&lt;p&gt;Cualquier pregunta sobre IoT Core, certificados, o el diseño de la tabla, la dejo abierta en los comentarios. ¡Nos vemos en la pista! 🏎️&lt;/p&gt;

</description>
      <category>aws</category>
      <category>iot</category>
      <category>serverless</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Introducción a los Data Lakes Parte 3</title>
      <dc:creator>Alvaro Garcia</dc:creator>
      <pubDate>Fri, 11 Sep 2026 15:21:57 +0000</pubDate>
      <link>https://dev.to/alvarongg/introduccion-a-los-data-lakes-parte-3-5f71</link>
      <guid>https://dev.to/alvarongg/introduccion-a-los-data-lakes-parte-3-5f71</guid>
      <description>&lt;p&gt;Hola a todos! Llegamos al final de la serie. En la &lt;a href="https://dev.to/alvarongg/introduccion-a-los-data-lakes-4946"&gt;primera parte&lt;/a&gt; vimos qué es un Data Lake y por qué importa. En la &lt;a href="https://dev.to/alvarongg/data-lake-serverless-en-aws-los-5-servicios-que-necesitas-saber"&gt;segunda parte&lt;/a&gt; conocimos los 5 servicios que forman el núcleo: S3, Glue, Athena, Lake Formation y Lambda.&lt;/p&gt;

&lt;p&gt;Hoy dejamos la teoría de lado. &lt;strong&gt;Vamos a construir el Data Lake completo&lt;/strong&gt;, con todo el código disponible en un repo abierto para que lo despliegues en tu propia cuenta de AWS en minutos.&lt;/p&gt;

&lt;p&gt;Acá estamos. Manos a la obra.&lt;/p&gt;




&lt;h2&gt;
  
  
  Qué vamos a construir
&lt;/h2&gt;

&lt;p&gt;El escenario es simple y realista: una empresa con varias sucursales que suben sus ventas diarias como archivos CSV. Cada vez que llega un archivo nuevo, el pipeline se dispara solo y transforma esos datos crudos en datos consultables con SQL.&lt;/p&gt;

&lt;p&gt;El flujo de punta a punta:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Un CSV llega a la &lt;strong&gt;raw zone&lt;/strong&gt; de &lt;strong&gt;S3&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;El evento &lt;strong&gt;PutObject&lt;/strong&gt; dispara una función &lt;strong&gt;Lambda&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Lambda lanza un &lt;strong&gt;Glue Job&lt;/strong&gt; que transforma el CSV a &lt;strong&gt;Parquet&lt;/strong&gt; y lo escribe en la processed zone&lt;/li&gt;
&lt;li&gt;Un &lt;strong&gt;Glue Crawler&lt;/strong&gt; mantiene actualizado el &lt;strong&gt;Data Catalog&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Athena&lt;/strong&gt; consulta los datos procesados con SQL estándar&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lake Formation&lt;/strong&gt; controla quién puede ver qué&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2trkundv6td5roqg8pit.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2trkundv6td5roqg8pit.png" alt="La arquitectura completa: cada servicio hace una sola cosa, y se conectan a través de eventos."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Todo lo que vimos en los posts anteriores, funcionando junto. Y lo mejor: &lt;strong&gt;todo el código está en un repo público&lt;/strong&gt;, definido como infraestructura con AWS CDK en Python.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  El repo: infraestructura como código con CDK
&lt;/h2&gt;

&lt;p&gt;En lugar de armar todo a mano desde la consola (clic acá, clic allá, y dentro de 6 meses nadie se acuerda de qué configuró), todo el Data Lake está definido como código con &lt;strong&gt;AWS CDK&lt;/strong&gt; en Python.&lt;/p&gt;

&lt;p&gt;📦 &lt;strong&gt;Repo:&lt;/strong&gt; &lt;a href="https://github.com/alvarongg/serverless-datalake-aws" rel="noopener noreferrer"&gt;github.com/alvarongg/serverless-datalake-aws&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;¿Por qué CDK y no la consola? Tres razones:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Reproducible:&lt;/strong&gt; lo despliegas en cualquier cuenta con un solo comando&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Versionado:&lt;/strong&gt; cada cambio en la infraestructura queda en el historial de Git&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Documentación viva:&lt;/strong&gt; el código &lt;em&gt;es&lt;/em&gt; la arquitectura — no hay un diagrama desactualizado en un wiki&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Si estás arrancando con Data Lakes, hacer todo por consola una vez es un buen ejercicio para entender las piezas. Pero para cualquier cosa que vaya a producción, infraestructura como código no es opcional.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Cómo está organizado el proyecto
&lt;/h2&gt;

&lt;p&gt;La estructura del repo es deliberadamente simple — &lt;strong&gt;un solo stack de CDK&lt;/strong&gt; que contiene todo el Data Lake:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;serverless-datalake-aws/
├── app.py                      # Entry point de CDK
├── datalake/
│   └── datalake_stack.py       # El stack completo: S3, Lambda, Glue, Athena, Lake Formation
├── lambda_src/
│   └── trigger_pipeline.py     # La función que escucha eventos de S3
├── glue_src/
│   └── transform_job.py        # El script del Glue Job (CSV → Parquet)
├── sample_data/
│   └── generate_ventas.py      # Generador de CSVs de ejemplo
├── requirements.txt
└── README.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;¿Por qué un solo stack? Para un proyecto de este tamaño, separar en múltiples stacks agrega complejidad sin beneficio. Un &lt;code&gt;cdk deploy&lt;/code&gt; y tenés el Data Lake entero corriendo. Cuando el proyecto crece (múltiples equipos, múltiples ambientes), ahí sí tiene sentido separar — pero ese es un problema que vas a tener recién cuando lo tengas.&lt;/p&gt;




&lt;h2&gt;
  
  
  Las piezas del stack
&lt;/h2&gt;

&lt;p&gt;Veamos qué define el stack, pieza por pieza.&lt;/p&gt;

&lt;h3&gt;
  
  
  El bucket con sus zonas
&lt;/h3&gt;

&lt;p&gt;El bucket de S3 se crea con las tres zonas que ya conocemos de la parte 2, separadas por prefijos:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;bucket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Bucket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DataLakeBucket&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;encryption&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BucketEncryption&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;S3_MANAGED&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;block_public_access&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BlockPublicAccess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BLOCK_ALL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;versioned&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fijate dos decisiones que no son negociables: &lt;strong&gt;cifrado activado&lt;/strong&gt; y &lt;strong&gt;acceso público bloqueado&lt;/strong&gt;. Son dos líneas de código que te ahorran un incidente de seguridad.&lt;/p&gt;

&lt;h3&gt;
  
  
  La Lambda que dispara el pipeline
&lt;/h3&gt;

&lt;p&gt;La función Lambda es la misma idea que vimos en la parte 2, pero ahora desplegada de verdad, con su trigger configurado en el código:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;trigger_fn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;TriggerPipelineFn&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;runtime&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;_lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PYTHON_3_12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;trigger_pipeline.lambda_handler&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;_lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Code&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_asset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;lambda_src&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_event_notification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;EventType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OBJECT_CREATED&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;s3n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;LambdaDestination&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;trigger_fn&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;NotificationKeyFilter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;raw/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El detalle importante es el &lt;code&gt;NotificationKeyFilter&lt;/code&gt;: solo los archivos que llegan a &lt;code&gt;raw/&lt;/code&gt; disparan el pipeline. Si Lambda escuchara todo el bucket, el propio Glue Job escribiendo en &lt;code&gt;processed/&lt;/code&gt; volvería a disparar el pipeline... y bienvenido al loop infinito que te explota la factura.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;El filtro de prefijo no es un detalle estético. Es la diferencia entre un pipeline event-driven y un loop infinito que procesa sus propias salidas.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  El Glue Job: de CSV a Parquet
&lt;/h3&gt;

&lt;p&gt;El Glue Job toma el CSV crudo y lo escribe como &lt;strong&gt;Parquet particionado por fecha&lt;/strong&gt; en la processed zone. ¿Por qué Parquet? Lo adelanté en el post anterior: Athena cobra por datos escaneados, y Parquet es un formato columnar comprimido que puede reducir el escaneo hasta un 80%.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyskdbem63nt95jarvkbg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyskdbem63nt95jarvkbg.png" alt="El mismo dataset, dos formatos: Parquet escanea una fracción de los datos y la factura de Athena lo agradece."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  El Crawler y la base de datos
&lt;/h3&gt;

&lt;p&gt;El stack también define la base de datos del Glue Data Catalog y un Crawler apuntando a la processed zone. Cada vez que el job termina, el catálogo se actualiza y las tablas nuevas (o las particiones nuevas) quedan disponibles para Athena sin intervención manual.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lake Formation y los permisos
&lt;/h3&gt;

&lt;p&gt;Por último, el stack registra el bucket en &lt;strong&gt;Lake Formation&lt;/strong&gt; y define los permisos sobre la base de datos. En un proyecto real acá es donde definirías qué equipo ve qué tablas y qué columnas — en el repo dejé el esqueleto con un permiso de ejemplo para que veas el patrón y lo extiendas según tu caso.&lt;/p&gt;




&lt;h2&gt;
  
  
  Desplegarlo en tu cuenta
&lt;/h2&gt;

&lt;p&gt;Con el repo clonado y tus credenciales de AWS configuradas:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Instalar dependencias&lt;/span&gt;
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt

&lt;span class="c"&gt;# Bootstrap de CDK (solo la primera vez en la cuenta/región)&lt;/span&gt;
cdk bootstrap

&lt;span class="c"&gt;# Desplegar el Data Lake completo&lt;/span&gt;
cdk deploy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Eso es todo. Un comando y CDK crea el bucket, la Lambda, el job, el crawler, la base de datos y los permisos. Al terminar, el output del deploy te muestra el nombre del bucket para que empieces a subir archivos.&lt;/p&gt;




&lt;h2&gt;
  
  
  El momento mágico
&lt;/h2&gt;

&lt;p&gt;Ahora viene la mejor parte. Generamos datos de ejemplo y los subimos a la raw zone:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Generar un CSV de ventas de ejemplo&lt;/span&gt;
python sample_data/generate_ventas.py

&lt;span class="c"&gt;# Subirlo a la raw zone&lt;/span&gt;
aws s3 &lt;span class="nb"&gt;cp &lt;/span&gt;ventas_2026-06-10.csv s3://&amp;lt;tu-bucket&amp;gt;/raw/ventas/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Y... no hacemos nada más. El archivo llega, Lambda lo detecta, el Glue Job lo transforma, el Crawler actualiza el catálogo. Todo solo.&lt;/p&gt;

&lt;p&gt;Unos minutos después, abrimos Athena y consultamos:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;sucursal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;monto&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;total_ventas&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;datalake_db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ventas&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;fecha&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'2026-06-10'&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;sucursal&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;total_ventas&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Subiste un CSV y unos minutos después lo estás consultando con SQL. Sin servidores, sin clusters, sin conexiones. &lt;strong&gt;Eso es un Data Lake serverless funcionando.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  ¿Cuánto costó la demo?
&lt;/h2&gt;

&lt;p&gt;Les debo el número porque es de los más compartibles del post: correr esta demo completa — deploy, varios archivos procesados, queries en Athena — cuesta &lt;strong&gt;centavos de dólar&lt;/strong&gt;. El desglose:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Servicio&lt;/th&gt;
&lt;th&gt;Uso en la demo&lt;/th&gt;
&lt;th&gt;Costo aproximado&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;S3&lt;/td&gt;
&lt;td&gt;Algunos MB almacenados&lt;/td&gt;
&lt;td&gt;&amp;lt; USD 0.01&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lambda&lt;/td&gt;
&lt;td&gt;Un puñado de invocaciones&lt;/td&gt;
&lt;td&gt;USD 0 (free tier)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Glue Job&lt;/td&gt;
&lt;td&gt;Pocos minutos de DPU&lt;/td&gt;
&lt;td&gt;USD 0.10–0.50&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Glue Crawler&lt;/td&gt;
&lt;td&gt;Ejecuciones cortas&lt;/td&gt;
&lt;td&gt;&amp;lt; USD 0.05&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Athena&lt;/td&gt;
&lt;td&gt;KBs escaneados (gracias, Parquet)&lt;/td&gt;
&lt;td&gt;&amp;lt; USD 0.01&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Importante: cuando termines de experimentar, &lt;code&gt;cdk destroy&lt;/code&gt; elimina todo el stack. No dejes recursos huérfanos dando vueltas — el Glue Crawler con schedule es el clásico que te aparece en la factura del mes siguiente.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Cierre de la serie
&lt;/h2&gt;

&lt;p&gt;Recapitulemos el camino que hicimos en estos tres posts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Parte 1:&lt;/strong&gt; qué es un Data Lake, sus desafíos y cómo fluyen los datos&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Parte 2:&lt;/strong&gt; los 5 servicios de AWS que forman el núcleo serverless&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Parte 3:&lt;/strong&gt; el pipeline completo, construido como código y desplegable en minutos&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;El repo queda abierto: &lt;a href="https://github.com/alvarongg/serverless-datalake-aws" rel="noopener noreferrer"&gt;github.com/alvarongg/serverless-datalake-aws&lt;/a&gt;. Si lo desplegás, lo rompés o lo mejorás, los issues y pull requests son bienvenidos.&lt;/p&gt;

&lt;p&gt;¿Y ahora qué? Tener el Data Lake funcionando es la mitad del trabajo. La otra mitad es &lt;strong&gt;evitar que con el tiempo se convierta en un Data Swamp&lt;/strong&gt; — y de eso, justamente, viene el próximo post.&lt;/p&gt;

&lt;p&gt;Si tienen preguntas, las leo en los comentarios.&lt;/p&gt;

&lt;p&gt;Abrazo grande, hasta la próxima 🚀&lt;/p&gt;

</description>
      <category>datalake</category>
      <category>aws</category>
      <category>serverless</category>
      <category>cdk</category>
    </item>
    <item>
      <title>Introducción a los Data Lakes Parte 2</title>
      <dc:creator>Alvaro Garcia</dc:creator>
      <pubDate>Wed, 19 Aug 2026 15:54:09 +0000</pubDate>
      <link>https://dev.to/alvarongg/introduccion-a-los-data-lakes-parte-2-4ca0</link>
      <guid>https://dev.to/alvarongg/introduccion-a-los-data-lakes-parte-2-4ca0</guid>
      <description>&lt;p&gt;En el &lt;a href="https://dev.to/alvarongg/introduccion-a-los-data-lakes-4946"&gt;post anterior&lt;/a&gt; exploramos qué es un Data Lake y por qué son tan importantes en el ecosistema de datos actual. Ahora es momento de ensuciarnos las manos y ver exactamente qué servicios de AWS necesitamos para construir un Data Lake completamente serverless y cómo orquestarlos.&lt;/p&gt;

&lt;h2&gt;
  
  
  Los Servicios Fundamentales
&lt;/h2&gt;

&lt;p&gt;Un Data Lake serverless en AWS se construye sobre cinco pilares fundamentales que trabajan en conjunto para crear una solución escalable y costo-eficiente: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Storage &lt;/li&gt;
&lt;li&gt;Procesamiento&lt;/li&gt;
&lt;li&gt;Catalogo&lt;/li&gt;
&lt;li&gt;Seguridad&lt;/li&gt;
&lt;li&gt;Explotación&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Amazon S3 - El Corazón del Storage
&lt;/h3&gt;

&lt;p&gt;S3 no es solo nuestro sistema de archivos, es la piedra angular del Data Lake. Aquí almacenamos tanto los datos crudos como los procesados, y su organización es crucial para el rendimiento y los costos.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Estructura de carpetas de un data lake estandar:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;data-lake-bucket/
├── raw/                    # Datos sin procesar
│   ├── year=2024/
│   ├── month=12/
│   └── day=15/
├── processed/              # Datos transformados
│   ├── bronze/             # Limpieza básica
│         ├── year=2024/
│         ├── month=12/
│         └── day=15/           
│   ├── silver/            # Transformaciones de negocio
│         ├── year=2024/
│         ├── month=12/
│         └── day=15/         
│   └── gold/              # Datos listos para consumo
│         ├── year=2024/
│         ├── month=12/
│         └── day=15/        
└── athena-results/        # Resultados de queries
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Notarás que todo el data lake se encuentra en un mismo bucket, esto es lo más recomendable ya que S3 tiene un límite de 100 bucket que podemos crear por cuenta (no importa la región, ya que S3 es un servicio global) &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Configuraciones clave en S3:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Versionado habilitado&lt;/strong&gt; para auditoría y rollback&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lifecycle policies&lt;/strong&gt; para optimizar costos (Standard → IA → Glacier)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Server-side encryption&lt;/strong&gt; con KMS para seguridad si es necesario.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cross-region replication&lt;/strong&gt; para disaster recovery&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  AWS Glue - El Motor de Transformación
&lt;/h3&gt;

&lt;p&gt;Glue es suite de servicios de data serverless que maneja tanto el descubrimiento de esquemas como las transformaciones de datos.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Componentes principales:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Glue Jobs&lt;/strong&gt;: Herramienta predilecta para ejecutar ETLs, nos permite procesar y transformar los datos de forma paralela, serverless y escalable usando Spark (en general Pyspark)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Glue Catalog&lt;/strong&gt;: Metastore centralizado que actúa como nuestro "diccionario o catálogo de datos"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Glue Crawlers&lt;/strong&gt;: Descubren automáticamente la estructura de los datos que dejamos en el storage que para este ejemplo es S3.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Un Glue Job típico se ve así:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;awsglue.transforms&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;awsglue.utils&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;getResolvedOptions&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pyspark.context&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SparkContext&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;awsglue.context&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;GlueContext&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;awsglue.job&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Job&lt;/span&gt;

&lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getResolvedOptions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;JOB_NAME&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="n"&gt;sc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SparkContext&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;glueContext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;GlueContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;spark&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;glueContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;spark_session&lt;/span&gt;
&lt;span class="n"&gt;job&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Job&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;glueContext&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;job&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;JOB_NAME&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Leer datos del catálogo
&lt;/span&gt;&lt;span class="n"&gt;datasource&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;glueContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create_dynamic_frame&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_catalog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;database&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mi_database&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;table_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;raw_data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Transformar datos
&lt;/span&gt;&lt;span class="n"&gt;transformed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ApplyMapping&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;datasource&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;mappings&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;old_column&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;string&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;new_column&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;string&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;timestamp&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;string&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;processed_date&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;timestamp&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Escribir a S3 en formato Parquet
&lt;/span&gt;&lt;span class="n"&gt;glueContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write_dynamic_frame&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_options&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;transformed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;connection_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;s3&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;connection_options&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;path&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;s3://mi-bucket/processed/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;parquet&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;job&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;commit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Amazon Athena - La Ventana de Consultas
&lt;/h3&gt;

&lt;p&gt;Athena nos permite consultar nuestros datos directamente desde S3 usando SQL estándar (ANSI SQL), sin necesidad de provisionar servidores y como si estuvieramos utilizando PrestoDB como motór de consultas.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ventajas clave:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pay-per-query&lt;/strong&gt;: Solo pagas por los datos escaneados&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integración nativa&lt;/strong&gt; con Glue Catalog&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Soporte para múltiples formatos&lt;/strong&gt;: Parquet, ORC, JSON, CSV&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Particionado automático&lt;/strong&gt; para optimizar performance&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Limitación a usuarios&lt;/strong&gt;: Se puede limitar la cantidad de querys, tiempo de consultas y volumen escaneado por usuario y grupo.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Seguridad integrada&lt;/strong&gt;: Se integra a otros servicios especialmente LakeFormation para la facil gobernanza de los datos.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ejemplo de query optimizada:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; 
    &lt;span class="n"&gt;region&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;total_events&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;AVG&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;revenue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;avg_revenue&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;processed_sales&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="nb"&gt;year&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'2024'&lt;/span&gt; 
    &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="k"&gt;month&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'12'&lt;/span&gt;
    &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;event_type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'purchase'&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;region&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;avg_revenue&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Importante - Nunca hagas un Select * From  porque va a ser la consulta más cara de tu vida.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  AWS Lambda - La Automatización Inteligente
&lt;/h3&gt;

&lt;p&gt;Lambda actúa como el pegamento que conecta todos los servicios, respondiendo a eventos y orquestando workflows complejos.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Casos de uso comunes:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Triggers de S3&lt;/strong&gt;: Procesar archivos automáticamente al llegar&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validación de datos&lt;/strong&gt;: Verificar calidad antes del procesamiento&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Notificaciones&lt;/strong&gt;: Alertar sobre fallos o completitud de procesos&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Orquestación&lt;/strong&gt;: Coordinar múltiples Glue Jobs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ejemplo de función Lambda que se ejecuta cuando llega un archivo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lambda_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;glue_client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;glue&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Extraer información del evento S3
&lt;/span&gt;    &lt;span class="n"&gt;bucket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Records&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s3&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;bucket&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Records&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s3&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;object&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;key&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="c1"&gt;# Iniciar Glue Job si es un archivo de datos
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;raw/&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;endswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;.json&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;glue_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start_job_run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;JobName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;process-raw-data&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;Arguments&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--input_path&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s3://&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--output_path&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s3://&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/processed/&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;statusCode&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Job iniciado: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;JobRunId&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  AWS CDK - Infrastructure as Code
&lt;/h3&gt;

&lt;p&gt;CDK nos permite definir toda nuestra infraestructura usando Python, manteniendo versionado y reproducibilidad.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;aws_cdk&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;Stack&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;aws_s3&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;aws_glue&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;glue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;aws_lambda&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;lambda_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;aws_s3_notifications&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;s3n&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DataLakeStack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Stack&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;construct_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nf"&gt;super&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;construct_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# S3 Bucket para el Data Lake
&lt;/span&gt;        &lt;span class="n"&gt;data_lake_bucket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Bucket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DataLakeBucket&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;versioned&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;lifecycle_rules&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;LifecycleRule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;move-to-ia&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;transitions&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
                        &lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Transition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                            &lt;span class="n"&gt;storage_class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StorageClass&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;INFREQUENT_ACCESS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                            &lt;span class="n"&gt;transition_after&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;days&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                        &lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="p"&gt;]&lt;/span&gt;
                &lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Glue Database
&lt;/span&gt;        &lt;span class="n"&gt;database&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;glue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CfnDatabase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DataLakeDatabase&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;catalog_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;database_input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;glue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CfnDatabase&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DatabaseInputProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data_lake_db&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Database for Data Lake&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Flujo de Datos Completo
&lt;/h2&gt;

&lt;p&gt;El flujo típico en nuestro Data Lake serverless sigue este patrón:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Ingesta&lt;/strong&gt;: Los datos llegan a S3/raw/ desde diversas fuentes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Detección&lt;/strong&gt;: S3 Event Notification dispara una Lambda&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Catalogación&lt;/strong&gt;: Glue Crawler descubre el esquema&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Procesamiento&lt;/strong&gt;: Glue Job transforma los datos&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Almacenamiento&lt;/strong&gt;: Datos procesados van a S3/processed/&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consulta&lt;/strong&gt;: Athena permite análisis ad-hoc&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Visualización&lt;/strong&gt;: Herramientas de BI consumen desde Athena&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Optimización de Costos
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Formatos de Archivo
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Parquet&lt;/strong&gt; para análisis columnar (reduce costos de Athena hasta 90%)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compresión GZIP&lt;/strong&gt; o &lt;strong&gt;Snappy&lt;/strong&gt; para reducir storage&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Particionado inteligente&lt;/strong&gt; por fecha/región/categoría&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Estrategias de Storage
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;S3 Intelligent Tiering&lt;/strong&gt; para archivos con patrones de acceso variables&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lifecycle policies&lt;/strong&gt; para mover datos antiguos automáticamente&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Spot instances&lt;/strong&gt; en Glue para workloads no críticas&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Monitoreo y Alertas
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# CloudWatch Custom Metrics en Lambda
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;

&lt;span class="n"&gt;cloudwatch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;cloudwatch&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;put_custom_metric&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;metric_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;unit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Count&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;cloudwatch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put_metric_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;Namespace&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;DataLake/Processing&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;MetricData&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;MetricName&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;metric_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Value&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Unit&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;unit&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Seguridad y Governance
&lt;/h2&gt;

&lt;h3&gt;
  
  
  IAM Roles y Políticas
&lt;/h3&gt;

&lt;p&gt;Cada servicio necesita permisos específicos y mínimos:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Glue Role&lt;/strong&gt;: Acceso a S3 y CloudWatch Logs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lambda Role&lt;/strong&gt;: Triggers de S3 y inicio de Glue Jobs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Athena Users&lt;/strong&gt;: Solo lectura en tablas específicas&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Encriptación End-to-End
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;S3&lt;/strong&gt;: Server-side encryption con KMS&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Glue&lt;/strong&gt;: Encriptación en jobs y catálogo&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Athena&lt;/strong&gt;: Encriptación de resultados&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Próximos Pasos
&lt;/h2&gt;

&lt;p&gt;En el siguiente post profundizaremos en &lt;strong&gt;AWS Glue en la práctica&lt;/strong&gt;, incluyendo:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Optimización de Glue Jobs para grandes volúmenes&lt;/li&gt;
&lt;li&gt;Testing y debugging de transformaciones&lt;/li&gt;
&lt;li&gt;Patrones avanzados de ETL&lt;/li&gt;
&lt;li&gt;Integración con herramientas de CI/CD&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Un Data Lake serverless en AWS no es solo una colección de servicios, es un ecosistema integrado que, bien diseñado, puede escalar desde gigabytes hasta petabytes manteniendo costos controlados y performance óptimo.&lt;/p&gt;

&lt;p&gt;¿Implementaste alguna de estas arquitecturas? ¿Qué desafíos te encontraste? &lt;/p&gt;

&lt;p&gt;Contame en los comentarios tu experiencia con tus Data Lakes.&lt;/p&gt;

</description>
      <category>dataengineering</category>
      <category>datalake</category>
      <category>aws</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Introducción a los Data Lakes</title>
      <dc:creator>Alvaro Garcia</dc:creator>
      <pubDate>Sun, 21 Jan 2024 23:36:23 +0000</pubDate>
      <link>https://dev.to/alvarongg/introduccion-a-los-data-lakes-4946</link>
      <guid>https://dev.to/alvarongg/introduccion-a-los-data-lakes-4946</guid>
      <description>&lt;p&gt;Hola a todos como post inaugural en esta comunidad me gustaría contarles un poco sobre Data Lakes de una forma un poco más general. &lt;/p&gt;

&lt;h2&gt;
  
  
  Que es un DATA LAKE ?
&lt;/h2&gt;

&lt;p&gt;Un data lake es un gran repositorio de datos estructurados y no estructurados, es donde podemos volcar todos los datos que genera la compañia. &lt;/p&gt;

&lt;p&gt;En general los data lakes son estructuras bastante mas grandes en cuanto a volumen y capacidad de procesamiento que una base de datos normal.&lt;/p&gt;

&lt;p&gt;Los data lakes y especialmente los data lakes en aws cobraron una notoriedad muy grande en los ultimos años, ya que nos permiten escalar en volumen de datos y capacidad de procesamiento de manera eficiente y a costos sensiblemente más bajos que con los métodos tradicionales.&lt;/p&gt;

&lt;h1&gt;
  
  
  Flujo de los datos
&lt;/h1&gt;

&lt;p&gt;Como gran sistema de procesamiento y almacenamiento de información el data lake tiene 3 objetivos principales.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4rbaoh0cn4ct0fg33gkt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4rbaoh0cn4ct0fg33gkt.png" alt=" " width="793" height="290"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Crecimiento de los datos
&lt;/h1&gt;

&lt;p&gt;Otra cuestión importante que tienen los data lakes es el crecimiento de los datos, en un data lake podemos almacenar datos de practicamente cualquier formato: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Actividad de los usuarios/clientes&lt;/li&gt;
&lt;li&gt;Sensores IOT&lt;/li&gt;
&lt;li&gt;Mensajeria y Redes sociales&lt;/li&gt;
&lt;li&gt;Emails&lt;/li&gt;
&lt;li&gt;Fotos&lt;/li&gt;
&lt;li&gt;Videos&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6zhq2l0s9rklzomqy44p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6zhq2l0s9rklzomqy44p.png" alt=" " width="434" height="346"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Estos datos pueden crecer muchisimo dependiendo del rubro y caso de uso, pero uno siempre piensa que tiene muchos menos datos de los que cree.&lt;/p&gt;

&lt;h1&gt;
  
  
  Desafios en la administración de un Data Lake
&lt;/h1&gt;

&lt;p&gt;Algunos de los desafios que plantea la administración de un Data Lake se pueden ver a simple vista, mientras que otros surgen de el descontrol de los primeros. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6m9bshrtkrn9w5l2ly4d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6m9bshrtkrn9w5l2ly4d.png" alt=" " width="800" height="290"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;En primer lugar tenemos que un data lake está en constante crecimiento y de manera exponencial algo que al final del día debe estar ordenado y aplicado de manera costo eficiente.&lt;/p&gt;

&lt;p&gt;Luego tenemos las fuentes de datos, sistemas de archivos, apis, bases de datos, sistemas enlatados, streaming de video, redes sociales. No hay límite en cuanto desde donde podemos recibir información lo que nos obliga a mantenernos versátiles. &lt;/p&gt;

&lt;p&gt;Como tercer punto tenemos la diversidad de esos mismos datos como vimos anteriormente, cualquier tipo de archivo del que podamos extraer un dato califica para ingresar a un data lake.&lt;/p&gt;

&lt;p&gt;y como último desafio tenemos los más importantes a mi parecer, la cantidad de usuarios que van a utilizar el data lake y luego la capacidad de integración que requiera nuestro  lago de datos con sistemas externos ya sea para analizar y/o visualizar información. &lt;/p&gt;

&lt;h2&gt;
  
  
  Arquitectura de un Data Lake en AWS
&lt;/h2&gt;

&lt;p&gt;Ahora que ya sabemos que es y porqué es tan importante un data lake hoy en día, vemos cual es la arquitectura mínima que debemos crear para que nuestro proyecto califique como un data lake. &lt;/p&gt;

&lt;h1&gt;
  
  
  Canalización del análisis de datos
&lt;/h1&gt;

&lt;p&gt;Definamos entonces cual es el proceso que debe seguir un dato al momento de ingresar en un data lake y como este debe actuar para que al final de todo ese camino obtengamos información.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F67o9537cmxcnt6mh0y2m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F67o9537cmxcnt6mh0y2m.png" alt=" " width="799" height="227"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Como pueden ver el proceso está separado en pocos pasos:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Recopilar&lt;/strong&gt;: Aqui es donde aplicamos las tecnicas necesaria para que el dato viaje a nuestra infraestructura, ya sea que nosotros tengamos que ir a buscarla como tambien proveer un método que le permita a los proveedores de datos enviarlos al data lake. Una vez que el dato llega se almacena crudo. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Procesar y Analizar&lt;/strong&gt;: En esta estapa vamos a tomar los datos crudos y los trabajaremos de manera que podamos obtener finalmente información relevante donde nuevamente vamos a almacenar la información en el repositorio correspondiente.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Visualizar&lt;/strong&gt;: Por ultimo una vez que ya tenemos información debemos utilizar algun tipo de herramienta que permita al usuario visualizarla y analizarla de una manera que se ajuste a su caso de uso. Quizá con una herramienta de BI para un usuario mas ejecutivo o algo más potente como una jpyter notebook para un analista de datos. &lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;La clave de este proceso es entender segun el dato y el caso de uso con que velocidad necesitamos tenerlo disponible desde su momento de creación. &lt;strong&gt;Así definiremos el balance entre costo y latencia/rendimiento.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Próximos pasos
&lt;/h2&gt;

&lt;p&gt;En el siguiente post vamos a repasar los servicios de aws que necesitamos minimamente para montar un Data Lake completamente Serverless.&lt;/p&gt;

</description>
      <category>datalake</category>
      <category>aws</category>
      <category>spanish</category>
      <category>dataengineering</category>
    </item>
  </channel>
</rss>
