posts/Uem5nmIcNIkSPxvaciBaD9b2LkxK4tYHmtHlpH02.png

Manejo de errores en ETL con Laravel: resiliencia en procesos de datos

Aprende cómo manejar errores en procesos ETL con Laravel para evitar datos corruptos, fallos silenciosos y reprocesos caóticos. Estrategias reales para jobs, batch y flujos de datos.

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:

  1. Extract
    Leer datos (CSV, API, DB externa, archivos)

  2. Transform
    Validar, normalizar, mapear, calcular

  3. Load
    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í 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:

  • PENDING

  • RUNNING

  • COMPLETED

  • COMPLETED_WITH_ERRORS

  • FAILED

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.

Comparte esta publicación

0 comentarios

Únete a la conversación y comparte tu experiencia.

Dejar un comentario

Comparte dudas, propuestas o mejoras para la comunidad.