pg_cron en Supabase: El Patrón que Convierte Tu Base de Datos en un Orquestador Autônomo

pg_cron en Supabase: El Patrón que Convierte Tu Base de Datos en un Orquestador Autônomo

Programming· 6 min read

El 90% de los Desarrolladores Usa pg_cron Solo Para Vacíos a las 3 AM

Vuestra base de datos Supabase lleva años haciendo una sola cosa con pg_cron: limpiar tablas temporales a medianoche.

Mientras tanto, estáis ignorando un orchestrator(event-driven) más potente que la mayoría de CI/CD schedulers que pagáis en servicios externos.

El problema real no es que pg_cron no sirva para más. Es que estáis tratándolo como un timer cuando es un cerebro.

La combinación de pg_cron + pg_net transforma Postgres en un sistema que no solo almacena datos — ejecuta pipelines completos, llama Edge Functions, y encadena trabajos de forma reactiva sin una capa de aplicación separada.

Por Qué Vuestros Cron Jobs Son Brite y Propensos a Fallar

Lo que hacéis la mayoría:

  1. Programáis un cron job a las 3 AM
  2. Ejecutáis una query de cleanup
  3. Olvidáis el job hasta que alguien se queja de datos faltantes

Este enfoque tiene tres fallos catastróficos.

Primer fallo: Fire-and-forget sin observabilidad. Si el job falla, no os enteráis hasta horas después. No hay retry automático, no hay logs centralizados, no hay circuit breakers.

Segundo fallo: jobs independientes que no se comunican. Job A hace su trabajo. Job B hace el suyo. Si Job B necesita el output de Job A, dependéis de timing exacto en vez de dependencias reales.

Tercer fallo: polling everywhere. Cuando Job B necesita saber si Job A terminó, la solución más común es hacer que B pregunte cada 5 minutos hasta que A responda. Esto crea load spikesauto-infligidos y llena la base de datos de queries inútiles.

ENFOQUE CONVENCIONAL (brite):

[@portabletext/react] Unknown block type "code", specify a component for it in the `components.types` prop

ENFOQUE REACTIVO (El Patrón de Cascada Autónoma):

[@portabletext/react] Unknown block type "code", specify a component for it in the `components.types` prop

pg_cron No Es Un Scheduler. Es Un Orquestador Event-Driven

pg_cron ejecuta como background worker dentro del proceso de Postgres. Zero network hops, zero autenticación externa, zero overhead de conexión.

Esto cambia la arquitectura.

Un scheduler externo (cron en una VM, cloud scheduler) necesita connection strings, SSL setup, y manejo de reconexiones. pg_cron corre en el mismo proceso que vuestra base de datos — hereda el security model naturalmente y elimina surface area para fallos.

La asimetría de pg_net que lo cambia todo:

La mayoría reach para LISTEN/NOTIFY cuando quieren patrones reactivos. pg_net llena otro nicho. Hace llamadas HTTP no-bloqueantes que devuelven inmediatamente un job_id — la función llamada completa sin esperar la respuesta HTTP.

Esto habilita patrones imposibles con NOTIFY solo:

→ Trigger a external APIs (Slack, Zapier, webhooks personalizados) desde dentro de un cron job

→ Llamar Edge Functions de Supabase que ellos mismos schedulan nuevos cron jobs

→ Crear autonomous feedback loops sin infraestructura adicional

El Framework de Cascada Autónoma: 5 Fases Para Pipelines Sin polling

Fase 1: Habilitad las Extensiones

Antes de nada, activad pg_cron y pg_net en vuestro proyecto Supabase:

[@portabletext/react] Unknown block type "code", specify a component for it in the `components.types` prop

Fase 2: Diseñad Vuestro Pipeline Como DAG

Mapad vuestras jobs como un Directed Acyclic Graph. Cada job escribe su estado y resultados en una tabla de control que las jobs downstream pueden consultar.

[@portabletext/react] Unknown block type "code", specify a component for it in the `components.types` prop

Fase 3: Implementad Llamadas Reactivas con pg_net

En vez de polling, la job upstream hace trigger directo a la downstream:

[@portabletext/react] Unknown block type "code", specify a component for it in the `components.types` prop

Fase 4: Instrumentad Observabilidad

pg_cron almacena todo en cron.job_run_details. Construid un dashboard básico:

[@portabletext/react] Unknown block type "code", specify a component for it in the `components.types` prop

Fase 5: Circuit Breakers y Retry Condicional

Cada job debe verificar el estado de la job anterior antes de ejecutarse:

[@portabletext/react] Unknown block type "code", specify a component for it in the `components.types` prop

El Thundering Herd Que Os Estáis Creando

Si scheduleáis 50 cron jobs que todas pegan la misma query resource-heavy al mismo minuto (e.g., cada hora en :00), estáis creando un thundering herd dentro de la base de datos.

Cómo prevenirlo:

  1. Staggered schedules: No pongáis todas las jobs en minuto :00. Distribuidlas entre :00, :02, :04, :06...
  2. Advisory locks: Para jobs que no pueden correr concurrentemente:
[@portabletext/react] Unknown block type "code", specify a component for it in the `components.types` prop
  1. Condition-based triggers: Solo ejecutar cuando hay datos nuevos, no en timer fijo:
[@portabletext/react] Unknown block type "code", specify a component for it in the `components.types` prop

Objections: Por Qué No Usar Edge Functions con Cron Triggers

"¿No puedo usar Edge Functions con cron triggers en vez de pg_cron?"

Edge Functions resuelven otro problema: corren fuera de la base de datos.

Si vuestra job es principalmente SQL — cleanup, agregación, refresh de materialized views — ejecutarla dentro de Postgres con pg_cron evita overhead de serialización/deserialización y mantiene consistencia transaccional.

El patrón híbrido es más potente que cualquiera de los dos solos:

  • pg_cron para trabajo SQL pesado
  • pg_net → Edge Function para side effects externos

Objections: ¿Y Redis Queue o RabbitMQ?

"¿No son más fiables los queue-based systems?"

Esto missing the point. pg_cron + pg_net no compite con sistemas de queue completos. Reemplaza el caso común donde equipos montan infraestructura de queue que no necesitan.

Para el 90% de pipelines recurrentes (ETL, cleanup, reporting, webhook calls), el ecosistema built-in de Postgres es suficiente.

El momento que necesitéis retry logic con dead-letter queues o exactly-once delivery semantics — sí, reached para una queue apropiada. Pero empezad simple.

Objections: Single Point of Failure

"Si Postgres cae, mi scheduler cae."

Esto es verdad pero misleading. Si Postgres está caído, vuestra aplicación ya está caída independientemente de dónde corra el scheduler.

La pregunta real es si el scheduler recupera correctamente cuando Postgres reinicia. pg_cron sí lo hace — almacena schedules en cron.job.

Si necesitáis el scheduler durante maintenance windows, un enfoque híbrido tiene sentido: pg_cron para ops normales, fallback externo para mantenimiento.

Conclusión

Vuestra base de datos Supabase es más inteligente de lo que le estáis dejando ser.

pg_cron + pg_net no es solo un scheduler. Es un orchestrator event-driven que puede transformar cómo vuestra aplicación reacciona a datos, eventos, y condiciones externas — sin una capa de aplicación separada, sin polling, sin servicios externos adicionales.

Key takeaways:

→ pg_cron corre como background worker dentro de Postgres — zero network hops, zero overhead

→ pg_net habilita HTTP requests asíncronos que no bloquean — triggers reactivos en vez de polling

→ El Patrón de Cascada Autónoma elimina dependencies de timing entre jobs

→ Instrumentad siempre con observabilidad desde el día uno

→ Los circuit breakers previenen cascadas de fallo cuando una job upstream falla

La próxima vez que reachéis para un servicio externo de scheduling, preguntáoslo: ¿no podría hacerlo mi base de datos?

Spoiler: probablemente sí.

Artículos relacionados

---

¿Quieres recibir contenido como este cada semana? Suscríbete a mi newsletter

Brian Mena

Brian Mena

Software engineer building profitable digital products: SaaS, directories and AI agents. All from scratch, all in production.

LinkedIn