Back to 0cron
0cron

Monitoreo heartbeat: cuando tu tarea deberia hacerte ping

Como 0cron implementa monitoreo heartbeat -- cron inverso donde tus tareas hacen ping a una URL, y el silencio dispara alertas. 105 lineas de Rust.

Juste A. Gnimavo (Thales) & Claude | March 26, 2026 4 min 0cron
EN/ FR/ ES
0cronmonitoringheartbeatrustpostgresqlalerting

La mayor parte de 0cron funciona en una direccion: llamamos a tus endpoints en un horario. Pero hay una clase de problemas donde la direccion necesita invertirse. Tienes un script de respaldo ejecutandose en tu propio servidor. Un pipeline de CI que deberia completarse cada hora. Una tarea de sincronizacion de datos gestionada por un servicio de terceros. No puedes apuntar 0cron a estas porque no controlas su invocacion. Lo que necesitas saber es si siguen ejecutandose.

Esto es monitoreo heartbeat, y esta incorporado en 0cron como funcionalidad de primera clase. El concepto es simple: te damos una URL. Tu tarea hace ping a esa URL cuando se completa. Si no recibimos un ping dentro de la ventana esperada mas un periodo de gracia, te alertamos. Silencio significa fallo.

La implementacion completa son 105 lineas de Rust.

El modelo de datos

sqlCREATE TABLE monitors (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    team_id UUID REFERENCES teams(id),
    name VARCHAR(255) NOT NULL,
    ping_token VARCHAR(64) UNIQUE NOT NULL,
    schedule_cron VARCHAR(100) NOT NULL,
    grace_period_seconds INTEGER DEFAULT 300,
    timezone VARCHAR(50) DEFAULT 'UTC',
    status VARCHAR(20) DEFAULT 'active',
    last_ping_at TIMESTAMPTZ,
    notification_config JSONB,
    created_at TIMESTAMPTZ DEFAULT NOW()
);

ping_token es una cadena hexadecimal unica de 64 caracteres (32 bytes aleatorios). Tokens en lugar de IDs de monitor por dos razones: los tokens son imposibles de adivinar y desacoplan el endpoint de ping del modelo de datos interno.

grace_period_seconds predeterminado en 300 (5 minutos). Una tarea que se ejecuta a las 2:00am y hace ping a las 2:04am no esta retrasada -- esta dentro de tolerancia.

last_ping_at es anulable. Un monitor nuevo nunca ha recibido un ping. No alertamos en monitores que nunca han hecho ping.

Generacion de tokens

rustfn generate_ping_token() -> String {
    use rand::Rng;
    let mut rng = rand::thread_rng();
    let bytes: Vec<u8> = (0..32).map(|_| rng.gen()).collect();
    hex::encode(bytes)
}

32 bytes aleatorios, codificados en hexadecimal a 64 caracteres. Eso son 256 bits de entropia -- mas combinaciones posibles que atomos en el universo observable.

Registro de pings

rustpub async fn record_ping(ping_token: &str, db: &PgPool) -> AppResult<()> {
    let now = Utc::now();
    let result = sqlx::query("UPDATE monitors SET last_ping_at = $1 WHERE ping_token = $2")
        .bind(now).bind(ping_token).execute(db).await?;
    if result.rows_affected() == 0 {
        return Err(AppError::NotFound(format!("Monitor with token '{ping_token}' not found")));
    }
    Ok(())
}

Una sola sentencia SQL. El endpoint de la API es GET /v1/ping/{token}. Si, GET, no POST. Las solicitudes GET son la solicitud HTTP mas simple posible. Funcionan con curl, con wget, e incluso pegando la URL en un navegador.

Deteccion de monitores vencidos

rustpub async fn check_monitors(db: &PgPool) -> AppResult<Vec<Monitor>> {
    let overdue = sqlx::query_as::<_, Monitor>(
        "SELECT * FROM monitors WHERE status = 'active'
         AND last_ping_at IS NOT NULL
         AND last_ping_at + (grace_period_seconds || ' seconds')::interval < NOW()",
    ).fetch_all(db).await?;
    Ok(overdue)
}

La expresion last_ping_at + (grace_period_seconds || ' seconds')::interval construye una marca de tiempo que representa "el ultimo ping mas el periodo de gracia." La aritmetica de intervalos de PostgreSQL es una funcionalidad de primera clase.

Casos de uso

Tareas de respaldo. Anade curl https://0cron.dev/v1/ping/TOKEN como la ultima linea de tu script. Si el respaldo falla, el curl nunca se ejecuta, y 0cron te alerta.

Pipelines CI/CD. Anade un ping al final del pipeline, establece el periodo de gracia a 1800 segundos. Si un despliegue se cuelga, la falta de ping dispara una alerta.

Salud de servicios externos. Monitoreo para un servicio que no controlas.

Dispositivos IoT. Un nodo sensor que deberia reportar cada 15 minutos. Si se calla, el patron de ping-y-silencio funciona.

105 lineas

Todo el monitoreo heartbeat -- modelo de datos, generacion de tokens, registro de pings, deteccion de vencidos -- son 105 lineas de Rust. Esto es posible porque tomamos decisiones agresivas de alcance. Una marca de tiempo en lugar de una tabla de historial. GET en lugar de POST. Periodos de gracia fijos en lugar de umbrales adaptativos.


Este es el articulo 8 de 10 en la serie "Como construimos 0cron".

Share this article:

Responses

Write a response
0/2000
Loading responses...

Related Articles

Thales & Claude deblo

El segfault que no era nuestro: cómo lanzamos el tracking del día de lanzamiento de Déblo en la noche del despliegue — analítica condicionada por entorno, atribución nativa de las tiendas, tres bugs que el compilador no podía ver y un build sin memoria que diagnosticamos en lugar de revertir

El 1 de julio de 2026 — el día del lanzamiento — el riesgo nunca fue el texto. Era que las campañas de pago salieran a ciegas. Este es el build-log de cómo desplegamos la analítica y la atribución de instalaciones de Déblo como código en la noche del lanzamiento: etiquetas GA4, Meta y LinkedIn condicionadas por entorno que se despliegan sin riesgo antes de que existan las cuentas publicitarias; atribución enrutada por los canales nativos de las tiendas en lugar del pixel web; una auditoría adversarial que atrapó tres bugs que tanto el typechecker como el build dieron por buenos; y un despliegue en Easypanel que hizo segfault en el primer build — que demostramos que no era nuestro código antes de tocar una sola línea.

18 min Jul 1, 2026
deblolaunch-dayclaude-opus-4.8claude-code +26
Thales & Claude thales

Trece agentes, cuarenta y tres minutos: la primera sesión Workflow de Claude Fable 5, y lo que un script de orquestación determinista cambia en los builds multiagente

Un prompt, trece agentes, cuarenta y tres minutos: la primera sesión de producción con Claude Fable 5 y la herramienta Workflow de Claude Code entregó un sitio web de producción completo de siete páginas más un endpoint backend de captura de leads, en un solo commit. La bitácora: el script de orquestación determinista, el patrón de inyección de contrato entre fases, la economía por agente del fan-out paralelo, y el suspenso del límite de sesión que el diario de reanudación convirtió en un no-evento.

23 min Jun 12, 2026
claude-fable-5claude-codeworkflow-toolmulti-agent +10
Thales & Claude casp

La puerta detectó su propia deriva: un día dentro de CASP con Claude Fable 5

Le entregamos al modelo Claude más autónomo hasta la fecha las llaves de CASP — la CLI open source que mantiene honestos a los agentes de código IA frente a git — con la autoridad de rechazar nuestra propia roadmap. Rechazó cinco cosas, encontró dos bugs reales en el validador al hacerle dogfooding, los corrigió bajo una puerta de dos auditores, y dejó casp check completamente en verde sobre su propio repositorio por primera vez. CASP 0.3.0 es el resultado.

15 min Jun 10, 2026
caspzerosuiteworkflowai-cto +9