Mantenimiento de logs en Supabase: Edge Functions y Cron Jobs

Tras trabajar en varios proyectos con backend en Supabase, repetí el mismo problema silencioso de disco: tablas de logs que crecen sin límite. Aquí está la función cleanup_logs() y cómo operarla con seguridad.

EP

Ender Puentes

Contexto: cómo llegué a esto

En los últimos meses he trabajado en distintos proyectos cuyo backend corre en Supabase. En varios de ellos las Edge Functions son pieza central — webhooks, tareas asíncronas, integraciones con servicios externos — y en otros también hay Cron Jobs con pg_cron. Todo funcionaba bien hasta que empecé a notar un patrón: con el tiempo, la base de datos se acercaba al límite de disco sin que el volumen de datos de negocio lo explicara.

La causa era la acumulación de logs: registros de ejecución que Supabase escribe directamente en PostgreSQL y que no se eliminan automáticamente desde el panel de administración.

La documentación oficial lo confirma:

  • Edge Functions: los logs viven en el schema edge_logs y en net._http_response.
  • Cron Jobs (pg_cron): cada ejecución genera entradas en cron.job_run_details.

Sin mantenimiento activo, esas tablas crecen de forma indefinida. En el peor escenario, la base entra en modo solo lectura por falta de espacio en disco — interrupción severa del servicio.

Este documento registra el problema, la función cleanup_logs() que implementamos como solución, y cómo operarla de forma segura en cualquier proyecto Supabase con Edge Functions o Cron Jobs.

El problema

  • cron.job_run_details · pg_cron — Historial de ejecuciones: start_time, status, command, return_message, etc.
  • net._http_response · pg_net — Respuestas HTTP de invocaciones Edge Function: created, status, headers, body, etc.

Cada invocación de Edge Function y cada ejecución de un cron job agrega filas. No hay botón de «limpiar logs» en la interfaz de Supabase. El crecimiento es silencioso hasta que el disco se agota.

Consecuencias observadas:

  • Consumo progresivo del espacio asignado al proyecto.
  • Degradación del rendimiento en consultas que tocan esas tablas.
  • Riesgo de modo solo lectura cuando el disco llega al límite.

La solución: cleanup_logs()

Función PL/pgSQL con SECURITY DEFINER que elimina registros más antiguos que un umbral configurable (por defecto: 12 horas).

sql
CREATE OR REPLACE FUNCTION cleanup_logs()
RETURNS void
SET search_path = 'public'
AS $$
BEGIN
    -- Limpiar logs de cron jobs
    DELETE FROM cron.job_run_details
    WHERE start_time < NOW() - INTERVAL '12 hours';

    -- Limpiar logs de respuestas HTTP (Edge Functions)
    DELETE FROM net._http_response
    WHERE created < NOW() - INTERVAL '12 hours';

    RAISE LOG 'Limpieza de logs completada a las %', NOW();
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;

COMMENT ON FUNCTION cleanup_logs() IS
  'Elimina logs antiguos de cron jobs y Edge Functions para evitar crecimiento descontrolado del disco';

Características técnicas

  • Seguridad — SECURITY DEFINER — ejecuta con privilegios del owner para acceder a los schemas cron y net
  • Alcance — search_path = 'public' — evita ambigüedad en la resolución de nombres
  • Retención — 12 horas por defecto; ajustable cambiando el intervalo en la función
  • Atomicidad — Corre dentro de una transacción; ambos DELETE son consistentes
  • Observabilidad — RAISE LOG deja rastro en los logs de PostgreSQL

Implementación paso a paso

1. Migración

bash
npx supabase migration new create_cleanup_logs_function

Agregar el SQL de la función al archivo generado y aplicar:

bash
# Local
npx supabase db reset

# Producción
npx supabase db push

2. Cron job de mantenimiento

Desde Supabase Dashboard → Database → Cron Jobs → Create Cron Job:

  • Nombre — cleanup-logs-maintenance
  • Schedule — */5 * * * * (cada 5 minutos)
  • Comando — SELECT cleanup_logs();
  • Activo — ✅ Habilitado

Nota: El schedule */5 * * * * mantiene las tablas acotadas en proyectos con alto tráfico. Para cargas menores, 0 */6 * * * (cada 6 horas) puede ser suficiente.

3. Verificación

sql
-- Job programado
SELECT jobname, schedule, active, command
FROM cron.job
WHERE jobname = 'cleanup-logs-maintenance';

-- Ejecuciones recientes
SELECT start_time, status, return_message, command
FROM cron.job_run_details
WHERE command LIKE '%cleanup_logs%'
ORDER BY start_time DESC
LIMIT 10;

Monitoreo

Consultas de diagnóstico

sql
-- Tamaño actual de logs de cron
SELECT
    COUNT(*) AS total_records,
    pg_size_pretty(pg_total_relation_size('cron.job_run_details')) AS table_size
FROM cron.job_run_details;

-- Tamaño actual de logs HTTP
SELECT
    COUNT(*) AS total_records,
    pg_size_pretty(pg_total_relation_size('net._http_response')) AS table_size
FROM net._http_response;

-- Distribución temporal (últimas 24 h)
SELECT
    DATE_TRUNC('hour', start_time) AS hour,
    COUNT(*) AS log_count
FROM cron.job_run_details
WHERE start_time > NOW() - INTERVAL '24 hours'
GROUP BY hour
ORDER BY hour DESC;

Alerta opcional por umbral

sql
CREATE OR REPLACE FUNCTION check_log_size_alert()
RETURNS void AS $$
DECLARE
    cron_count INTEGER;
    http_count INTEGER;
BEGIN
    SELECT COUNT(*) INTO cron_count FROM cron.job_run_details;
    SELECT COUNT(*) INTO http_count FROM net._http_response;

    IF cron_count > 10000 OR http_count > 50000 THEN
        RAISE WARNING 'Las tablas de logs crecen demasiado: cron=%, http=%',
            cron_count, http_count;
    END IF;
END;
$$ LANGUAGE plpgsql;

Programar check_log_size_alert() en un cron separado o invocarla desde el mismo job de mantenimiento si se necesita visibilidad temprana.

Consideraciones

⚠️ Advertencias

  • Irreversibilidad — Los logs eliminados no se recuperan. Exportar antes si se necesitan para auditoría.
  • Rendimiento — Los DELETE masivos pueden impactar brevemente la base. Ajustar retención y frecuencia según la carga.
  • Dependencias — Requiere las extensiones pg_cron y pg_net habilitadas (estándar en Supabase).

✅ Buenas prácticas

  • Retención gradual: empezar con 24–48 h y reducir solo si el disco lo exige.
  • Horarios de baja carga: en proyectos sensibles, programar limpiezas en ventanas de menor tráfico.
  • Monitoreo proactivo: revisar el tamaño de las tablas semanalmente hasta estabilizar el patrón de crecimiento.
  • Respaldo: considerar exportar logs críticos de forma periódica antes de la purga si hay requisitos de cumplimiento normativo.

Referencia rápida

text
Función:    cleanup_logs()
Retención:  12 horas (configurable)
Cron:       cleanup-logs-maintenance → SELECT cleanup_logs();
Tablas:     cron.job_run_details, net._http_response

Más notas sobre Arquitectura de Base de Datos.