Los procesos ETL (Extract, Transform, Load) son el corazón de muchos sistemas modernos:
importaciones, sincronizaciones, migraciones, integraciones externas y análisis de datos.
El problema no es construir un ETL que funcione.
El problema es construir uno que falle bien.
En Laravel, un ETL mal diseñado puede:
Fallar a mitad del proceso
Dejar datos incompletos
Reprocesar información incorrectamente
Ocultar errores críticos en producción
Este artículo se enfoca en cómo manejar errores correctamente en ETL, no en cómo evitarlos (porque van a ocurrir).
¿Qué caracteriza a un ETL en Laravel?
Un ETL típico en Laravel suele tener estas fases:
Extract
Leer datos (CSV, API, DB externa, archivos)Transform
Validar, normalizar, mapear, calcularLoad
Insertar o actualizar datos en la base local
Cada fase puede fallar por razones distintas, y no todas deben manejarse igual.
Error común: tratar el ETL como un solo bloque
Un error muy frecuente:
try { $this->extract(); $this->transform(); $this->load(); } catch (\Throwable $e) { Log::error($e); }
Problemas:
No sabes en qué fase falló
No sabes qué registro causó el error
No puedes reintentar parcialmente
El sistema queda ciego
En ETL, el contexto del error es tan importante como el error mismo.
Principio clave: errores por registro, no por proceso
Un ETL bien diseñado no falla completo por un solo registro (salvo casos críticos).
Ejemplo:
1,000 filas válidas
5 filas corruptas
Resultado esperado:
995 procesadas
5 registradas como error
El proceso continúa
Manejo de errores en la fase Extract
Errores comunes:
Archivo corrupto
API caída
Timeout
Credenciales inválidas
Estrategia:
Si Extract falla, el ETL no debe continuar
Es un error bloqueante
try { $rows = $this->extract(); } catch (\Throwable $e) { Log::critical('ETL extract failed', [ 'error' => $e->getMessage(), ]); return; }
Aquí sí se aborta todo.
Manejo de errores en Transform (el punto más crítico)
La transformación suele fallar por:
Datos inválidos
Campos faltantes
Tipos incorrectos
Reglas de negocio
Aquí NO se debe abortar todo el ETL.
foreach ($rows as $row) { try { $data = $this->transform($row); $this->load($data); } catch (\Throwable $e) { $this->registerError($row, $e); continue; } }
Esto permite:
Aislar errores
Continuar el flujo
Analizar después
Registro estructurado de errores ETL
Nunca basta con
Log::error().Un ETL serio necesita persistir errores:
ETLError::create([ 'source' => 'customers_import', 'reference' => $row['id'] ?? null, 'phase' => 'transform', 'payload' => json_encode($row), 'error' => $e->getMessage(), ]);
Beneficios:
Auditoría
Reprocesamiento
Métricas de calidad de datos
Manejo de errores en Load (base de datos)
Aquí entran:
Violaciones de unique
Constraints
Relaciones inexistentes
Deadlocks
Recomendación clave:
Load siempre dentro de transacción
Error → rollback solo de ese registro
try { DB::transaction(function () use ($data) { Model::updateOrCreate( ['external_id' => $data['external_id']], $data ); }); } catch (\Throwable $e) { $this->registerError($data, $e); }
ETL + Jobs: errores y reintentos
Cuando un ETL corre en Jobs:
El Job no debe fallar por errores de datos
Solo debe fallar por errores sistémicos
Ejemplo:
❌ Dato inválido → no fallar Job
❌ Constraint violado → no fallar Job
✅ DB caída → fallar Job
✅ API inaccesible → fallar Job
Esto evita:
Reintentos infinitos inútiles
Saturar colas
Reprocesar basura
Estados del proceso ETL
Un buen ETL tiene estados claros:
PENDINGRUNNINGCOMPLETEDCOMPLETED_WITH_ERRORSFAILED
Esto permite:
Monitoreo
Alertas
Reprocesos controlados
Reprocesamiento: el gran olvidado
Un ETL sin estrategia de reprocesamiento no está terminado.
Buenas prácticas:
Guardar payload original
Guardar fase de error
Permitir reprocesar solo fallidos
Nunca reprocesar todo a ciegas
Relación con idempotencia y transacciones
Este artículo cierra el círculo:
Idempotencia → evita duplicados
Transacciones → evita estados parciales
Manejo de errores ETL → evita caos operativo
Las tres cosas son inseparables en sistemas reales.
Conclusión
Un ETL robusto no es el que nunca falla.
Es el que:
Falla de forma controlada
Registra lo que falló
Continúa cuando puede
Permite corregir y reprocesar
Laravel es perfectamente capaz de manejar ETL complejos,
pero solo si los diseñas pensando en el error como parte del flujo, no como una excepción rara.