lección 6

Día 4 (mañana) — Algo se rompe en producción

Una alerta de Airflow a las 9:15 AM. Tu primer incidente de producción: diagnosticar un DAG que falló, comunicar al equipo y aplicar un fix rápido mientras planeas el definitivo.

55 min

### 9:15 AM — La alerta que te pone nervioso

Jueves por la mañana. Te sientas con el café, abres Slack y lo primero que ves es un mensaje en #data-alerts con un emoji rojo que no habías visto antes. Tu corazón se acelera un poco:

1#data-alerts 9:15 AM
2🚨 AIRFLOW ALERT — DAG: raw_orders_daily — FAILED
3Task: extract_orders failed at 2024-09-19 03:00 UTC
4Error: ConnectionRefusedError: [Errno 111] Connection refused
5Retries exhausted (3/3)
6Next retry: NONE (max retries reached)
7
8Dag run: scheduled__2024-09-19T03:00:00+00:00
9Duration before failure: 2m 34s

Tu primera alerta de producción — el DAG de pedidos no se ejecutó anoche

Esto significa que el pipeline que extrae los pedidos diarios de la base de datos de producción y los deja en el data lake... no se ejecutó anoche. Los datos de pedidos de ayer NO están en el lake. Cualquier dashboard que dependa de datos frescos está mostrando información de anteayer. No es tu DAG ni tu responsabilidad directa, pero estás a punto de aprender cómo se gestiona un incidente de verdad.

### 9:30 AM — Standup: Elena te asigna el diagnóstico

1Elena Torres 9:31 AM
2¿Alguien ha visto la alerta de raw_orders? Lleva fallando desde las 3AM.
3Raúl, ¿es tuyo ese DAG?
4
5Raúl Vega 9:34 AM
6sí, es mío... pero estoy en medio de lo del inventario
7la verdad no sé por qué falla, la semana pasada iba bien
8puede ser que RDS esté saturado?
9
10Elena Torres 9:36 AM
11tú, ¿puedes echarle un ojo? No te pido que lo arregles, solo
12que diagnostiques qué pasa y me digas. Si es algo de RDS escala a
13infra. Si es un bug en el DAG, dime qué ves.
14
15Tú 9:37 AM
16Dale, me pongo con ello ahora mismo.

Elena confía en ti para diagnosticar — no para arreglar (aún)

Nota lo que acaba de pasar: Elena te pidió DIAGNOSTICAR, no arreglar. Esto es importante. Como junior, tu trabajo en un incidente es investigar, documentar hallazgos y proponer opciones. No es aplicar un fix a ciegas que pueda empeorar las cosas. Esa distinción entre "investigar" y "actuar" es una de las cosas más importantes que aprenderás en tus primeros meses.

Consejo de senior: en un incidente de producción, la PEOR respuesta es entrar en pánico y hacer cambios sin entender el problema. He visto juniors que ante una alerta hacen un "fix rápido" que rompe 3 cosas más. La secuencia correcta es siempre: (1) entender qué hace el sistema, (2) leer los logs, (3) formular una hipótesis, (4) verificarla, (5) proponer solución. Nunca saltes al paso 5 directamente.

### Paso 1: Entender qué hace el DAG raw_orders_daily

Antes de mirar los logs, necesitas entender QUÉ se supone que hace este DAG. Abres el repositorio en GitHub y buscas el archivo del DAG:

1# dags/raw_orders_daily.py (simplificado)
2"""
3DAG: raw_orders_daily
4Schedule: Cada día a las 03:00 UTC
5Qué hace:
6 1. Conecta a PostgreSQL de producción (RDS)
7 2. Extrae todos los pedidos del día anterior
8 3. Los escribe como CSV en S3: raw/orders/{year}/{month}/{day}/
9 4. Envía un mensaje a #data-alerts si falla
10"""
11from airflow import DAG
12from airflow.operators.python import PythonOperator
13from datetime import datetime, timedelta
14import psycopg2
15import pandas as pd
16import boto3
17
18default_args = {
19 "owner": "raul.vega",
20 "retries": 3,
21 "retry_delay": timedelta(minutes=5),
22}
23
24def extract_orders(**context):
25 """Extrae pedidos del día anterior de PostgreSQL."""
26 execution_date = context["ds"]
27
28 # ⚠️ CONEXIÓN HARDCODEADA (esto lo verás después)
29 conn = psycopg2.connect(
30 host="freshmart-prod.xxxxx.eu-west-1.rds.amazonaws.com",
31 port=5432,
32 dbname="orders",
33 user="data_readonly",
34 password="FrM4rt_R0_2024!", # 🚩 CREDENCIAL EN EL CÓDIGO
35 )
36
37 query = f"""
38 SELECT * FROM orders
39 WHERE order_date = '{execution_date}'
40 """
41 df = pd.read_sql(query, conn)
42 conn.close()
43
44 # Escribir a S3
45 output_path = f"s3://freshmart-lake/raw/orders/{execution_date}/"
46 df.to_csv(f"{output_path}orders.csv", index=False)
47
48with DAG(
49 "raw_orders_daily",
50 default_args=default_args,
51 schedule_interval="0 3 * * *",
52 start_date=datetime(2024, 1, 1),
53 catchup=False,
54) as dag:
55 extract = PythonOperator(
56 task_id="extract_orders",
57 python_callable=extract_orders,
58 )

El DAG de Raúl: funcional pero con MUCHOS problemas de buenas prácticas

Al leer el código ya ves varios problemas (que no son la causa del fallo, pero que anotas mentalmente): la credencial está hardcodeada en el código (gravísimo), la query es vulnerable a inyección SQL, no hay validación del DataFrame antes de escribir. Pero ahora toca centrarse en el ERROR, no en refactorizar.

NUNCA pongas credenciales en el código. Ni en un script "temporal", ni en un notebook "de prueba", ni en un commit que "después borro". Las credenciales hardcodeadas son la causa número 1 de brechas de seguridad en empresas. Usa variables de entorno, AWS Secrets Manager, Airflow Connections o cualquier sistema de secrets — pero NUNCA el código fuente.

### Paso 2: Leer los logs del DAG en Airflow

Accedes a la interfaz web de Airflow (http://airflow.internal:8080 en la red de FreshMart) y navegas al DAG raw_orders_daily. Ves que el último run exitoso fue el martes 17 de septiembre. El del miércoles 18 falló. El del jueves 19 (hoy) también:

1# Logs del task extract_orders — run del 2024-09-19
2
3[2024-09-19 03:00:05] INFO - Starting task extract_orders
4[2024-09-19 03:00:05] INFO - Execution date: 2024-09-18
5[2024-09-19 03:00:06] INFO - Connecting to PostgreSQL at freshmart-prod.xxxxx.eu-west-1.rds.amazonaws.com:5432
6[2024-09-19 03:00:06] ERROR - Connection failed:
7 psycopg2.OperationalError: could not connect to server: Connection refused
8 Is the server running on host "freshmart-prod.xxxxx.eu-west-1.rds.amazonaws.com"
9 and accepting TCP/IP connections on port 5432?
10[2024-09-19 03:00:06] INFO - Retrying in 300 seconds (attempt 1/3)
11[2024-09-19 03:05:06] ERROR - Connection failed (retry 2/3): Connection refused
12[2024-09-19 03:10:06] ERROR - Connection failed (retry 3/3): Connection refused
13[2024-09-19 03:10:06] ERROR - Max retries reached. Task FAILED.
14
15# Logs del run anterior (2024-09-18, que también falló)
16[2024-09-18 03:00:06] ERROR - Connection failed:
17 psycopg2.OperationalError: FATAL: password authentication failed
18 for user "data_readonly"
19[2024-09-18 03:05:06] ERROR - password authentication failed (retry 2/3)
20[2024-09-18 03:10:06] ERROR - password authentication failed (retry 3/3)

Los logs revelan DOS errores distintos: primero auth failed, luego connection refused

### Paso 3: Formular hipótesis

Ahora piensas. Los logs te dicen dos cosas diferentes en dos días:

  1. 01.Miércoles 18: "password authentication failed" → las credenciales son incorrectas
  2. 02.Jueves 19: "Connection refused" → no puede ni llegar al servidor
  3. 03.El último run EXITOSO fue el martes 17 → algo cambió entre el martes y el miércoles

Hipótesis: alguien cambió las credenciales de la base de datos entre el martes y el miércoles. El miércoles el DAG intentó conectar con la contraseña vieja → "auth failed". Y el jueves, quizás RDS rechaza conexiones del usuario data_readonly porque está bloqueado por demasiados intentos fallidos → "connection refused".

### Paso 4: Verificar la hipótesis

Preguntas en el canal #infra (infraestructura) si alguien rotó credenciales esta semana:

1Tú 10:15 AM en #infra
2Hola equipo, estoy investigando un fallo en el DAG raw_orders_daily.
3Los logs muestran "password authentication failed" desde el miércoles.
4¿Se rotaron credenciales de RDS esta semana?
5
6Carlos (Infra) 10:22 AM
7Sí, rotamos las credenciales de todos los usuarios de solo lectura
8el martes por la noche. Es parte del protocolo trimestral de seguridad.
9Mandamos un email al canal de engineering el lunes avisando.
10Las nuevas credenciales están en AWS Secrets Manager:
11secret: freshmart/prod/rds/data_readonly
12
13Carlos (Infra) 10:23 AM
14¿El equipo de data no usa Secrets Manager? 🤔
15
16Tú 10:25 AM
17Aparentemente este DAG tiene las credenciales hardcodeadas en el código...
18Vamos a arreglarlo. Gracias Carlos!

Verificación: infra rotó credenciales y el DAG usa las antiguas hardcodeadas

Hipótesis confirmada. El problema es claro: las credenciales se rotaron el martes por la noche, el DAG tiene la contraseña vieja escrita directamente en el código, y como nadie del equipo de data leyó el email de aviso, nadie se enteró hasta que la alerta saltó el miércoles.

### Paso 5: Reportar a Elena con opciones

No reportas solo el problema — reportas el problema Y propones soluciones. Esto es lo que diferencia a un junior que investiga de un ingeniero que resuelve:

1Tú 11:22 AM
2Elena, encontré el problema del DAG raw_orders.
3No es que RDS esté caído — las credenciales se rotaron el martes
4por la noche (protocolo trimestral de seguridad de infra) y nadie
5avisó al equipo de data. El DAG usa las credenciales antiguas
6hardcodeadas en el script.
7
8Opciones:
91. Fix rápido (5 min): actualizar la contraseña en el código con la
10 nueva. Mismo problema la próxima vez que roten (en 3 meses).
112. Fix definitivo (2-3h): migrar la conexión a Airflow Connections
12 o leer de AWS Secrets Manager. Nunca más se rompe por rotación.
13
14¿Cómo prefieres que proceda?
15
16Elena Torres 11:25 AM
17Buena investigación. 👏
18Haz el fix rápido AHORA (que llevamos sin datos de pedidos desde
19ayer) y abre un ticket para el fix definitivo. Lo hacemos la semana
20que viene con calma.
21Y por favor, manda un mensaje a #data-team diciendo qué pasó y que
22ya está arreglado. El equipo necesita saber.

Reporta: problema + causa raíz + opciones con trade-offs. Esto es profesional.

Consejo de senior: cuando reportes un incidente a tu lead, SIEMPRE presenta opciones con trade-offs. No digas "se rompió, ¿qué hago?". Di "se rompió por X, puedo hacer A (rápido pero temporal) o B (más trabajo pero definitivo). ¿Cuál prefieres?". Esto demuestra que no solo investigaste sino que PENSASTE en la solución. Y le das a tu lead la información para tomar una decisión informada.

### Paso 6: Aplicar el fix rápido

Elena dijo "fix rápido ahora". Así que actualizas la credencial en el código del DAG. Sí, es feo. Sí, es temporal. Pero la prioridad ahora es RESTAURAR EL SERVICIO — los datos llevan 2 días sin actualizarse.

1# Fix rápido: actualizar la credencial (temporal)
2# Obtenemos la nueva credencial de AWS Secrets Manager
3# (en la terminal, con aws cli)
4
5# 🟦 Windows (PowerShell):
6# aws secretsmanager get-secret-value --secret-id freshmart/prod/rds/data_readonly --query SecretString --output text
7
8# 🍎 Mac (Terminal):
9# aws secretsmanager get-secret-value --secret-id freshmart/prod/rds/data_readonly --query SecretString --output text
10
11# Luego actualizamos el DAG con la nueva password:
12# (esto es el fix TEMPORAL — la semana que viene lo migramos a Connections)
13conn = psycopg2.connect(
14 host="freshmart-prod.xxxxx.eu-west-1.rds.amazonaws.com",
15 port=5432,
16 dbname="orders",
17 user="data_readonly",
18 password="NEW_FrM4rt_R0_Q4_2024!", # ← Nueva credencial (FIX TEMPORAL)
19)

Fix temporal: actualizar la credencial. La semana que viene se hace bien.

Haces el cambio, pusheas a una branch, abres PR (que Elena aprueba inmediatamente sin review detallada porque es urgente), mergeas y triggeas un re-run del DAG manualmente desde la UI de Airflow. A los 3 minutos el DAG está en verde. Los datos de ayer y hoy empiezan a fluir.

### Paso 7: Comunicar al equipo

Elena te pidió que comunicaras al equipo. No es opcional — es parte del protocolo de incidentes. El equipo necesita saber: (1) que hubo un problema, (2) cuál fue la causa, (3) que ya está resuelto, y (4) si hay impacto residual.

1Tú 12:05 PM en #data-team
2🔧 Resumen del incidente raw_orders_daily:
3
4**Qué pasó:** El DAG raw_orders_daily falló miércoles y jueves (2 días sin datos de pedidos nuevos).
5
6**Causa raíz:** Infra rotó las credenciales de RDS el martes noche (protocolo trimestral). El DAG tenía las credenciales hardcodeadas en el código → usaba las antiguas.
7
8**Fix aplicado:** Actualizada la credencial en el código. DAG re-ejecutado. Los datos de miércoles y jueves ya están en el lake.
9
10**Impacto:** Dashboards de pedidos mostraron datos de hace 2 días entre miércoles 3AM y hoy 12PM. Ya están actualizados.
11
12**Próximos pasos:** Ticket DATA-162 abierto para migrar la conexión a Airflow Connections / Secrets Manager (fix definitivo, sprint que viene).
13
14cc @elena.torres @raul.vega
15
16Elena Torres 12:08 PM
17Perfecto. Gracias por el resumen claro.
18
19Jorge Ruiz 12:10 PM
20Gracias! Entonces los números de pedidos de ayer en Metabase ya son correctos?
21
22Tú 12:11 PM
23Sí, el backfill ya corrió. Los datos de miércoles y jueves están en el lake.
24
25Raúl Vega 12:15 PM
26uf perdona, ese DAG lleva meses con las creds hardcodeadas 😅
27lo de Secrets Manager lo tenía en mi TODO... gracias por el ticket

Comunicar el incidente: claro, estructurado y con next steps

Observa la reacción del equipo: Elena agradece la claridad, Jorge verifica el impacto en SU trabajo (normal), y Raúl reconoce la deuda técnica. Nadie te culpa — investigaste, resolviste y comunicaste. Eso es lo que se espera.

### El ticket para el fix definitivo

Antes de seguir con tu trabajo del día, abres el ticket en Linear para que el fix definitivo no se pierda:

1╔══════════════════════════════════════════════════════════════╗
2║ TICKET: DATA-162 ║
3║ Título: Migrar credenciales de raw_orders_daily a ║
4║ Airflow Connections / AWS Secrets Manager ║
5╠══════════════════════════════════════════════════════════════╣
6║ Contexto: ║
7║ El DAG raw_orders_daily tiene credenciales de RDS ║
8║ hardcodeadas en el código. Cuando infra rotó las creds ║
9║ (protocolo trimestral), el DAG falló 2 días hasta que ║
10║ se detectó y se actualizó manualmente. ║
11║ ║
12║ Solución propuesta: ║
13║ 1. Crear una Airflow Connection tipo 'postgres' con las ║
14║ credenciales correctas ║
15║ 2. O: leer la credential de AWS Secrets Manager en runtime ║
16║ 3. Eliminar la password del código fuente ║
17║ 4. Verificar que no haya más DAGs con creds hardcodeadas ║
18║ ║
19║ Prioridad: Media (fix rápido ya aplicado) ║
20║ Asignado a: Por definir (¿Raúl? ¿Yo?) ║
21║ Estimación: 2-3 horas ║
22╚══════════════════════════════════════════════════════════════╝

El ticket documenta el problema para que el fix definitivo no se olvide

Los 8 pasos de un incidente: de la alerta a "resuelto y comunicado"

### Reflexión: lo que has aprendido sobre incidentes

Son las 12:30 y tu primer incidente de producción está resuelto. No era tu DAG, no era tu código, ni siquiera era un error de código — era un problema de configuración causado por falta de comunicación entre equipos. Y sin embargo, TÚ fuiste quien lo diagnosticó y resolvió. Eso es lo que hace un ingeniero de datos en la vida real.

  • Los fallos en producción son NORMALES — ocurren cada semana en cualquier empresa
  • La mayoría de fallos NO son bugs de código — son problemas de configuración, permisos, red o datos
  • Diagnosticar metódicamente (logs → hipótesis → verificación) te ahorra horas de prueba y error
  • Comunicar el incidente es parte del fix — si no avisas al equipo, el siguiente que mire los dashboards pensará que hay otro problema
  • Fix rápido + ticket definitivo es una estrategia válida cuando el servicio está caído

Un error que veo en juniors: resolver el incidente y NO comunicarlo. Piensas "ya está arreglado, no hace falta molestar". ERROR. El equipo necesita saber que hubo un problema, por cuánto tiempo, y si les afecta. Jorge revisó sus números de ayer pensando que eran correctos — si tú no avisas, él puede tomar decisiones basadas en datos obsoletos sin saberlo.

## ejercicios

[01]

Leer logs y diagnosticar un fallo de DAG

Se te dan los logs de un DAG de Airflow que falló. Analízalos y responde: (1) ¿Cuál es el error exacto?, (2) ¿Es un problema de código, de red, o de credenciales?, (3) ¿Qué preguntarías para confirmar tu hipótesis? Escribe un script que parsee los logs y extraiga las líneas de ERROR.

Cargando editor...
[02]

Implementar lectura segura de credenciales

Reescribe la función de conexión del DAG para que NO tenga credenciales hardcodeadas. Implementa dos opciones: (1) leer de variables de entorno, (2) leer de un diccionario que simula AWS Secrets Manager. La función debe intentar Secrets Manager primero y caer a variables de entorno como fallback.

Cargando editor...
[03]

Escribir el mensaje de comunicación del incidente

Escribe el mensaje que enviarías al canal #data-team después de resolver el incidente. Debe incluir: (1) Qué pasó (en una frase), (2) Causa raíz, (3) Fix aplicado, (4) Impacto (qué estuvo mal y por cuánto tiempo), (5) Próximos pasos. Escríbelo como texto plano tipo Slack — no como código.

Cargando editor...

Regístrate para guardar tu progreso.

## comentarios

Reporta erratas, ayuda a otros o comparte tu opinión. Sé constructivo.

Inicia sesión para comentar y responder.

cargando comentarios...