# 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.

## 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
```
