Vivere

Prefect

A deployment with no worker on its work pool goes Late, not Failed, and Late sends nothing by default.

Prefect's notifications fire on Failed and Crashed flow runs. A deployment whose work pool has no worker polling it does not fail: its runs sit in Late, indefinitely, and Late is not a notification state unless you build an automation for it. The same silence covers a flow.serve() process that died, a schedule someone paused in the UI, and a deployment that was never re-applied after a redeploy.

State hooks on the flow

import urllib.request
from prefect import flow

PING = "https://vivere.dev/p/<monitor-id>"

def ping(suffix="", body=None):
    try:
        urllib.request.urlopen(PING + suffix, data=body, timeout=10)
    except Exception:
        pass

@flow(
    name="nightly-etl",
    on_completion=[lambda flow, run, state: ping()],
    on_failure=[lambda flow, run, state: ping("/fail", str(state).encode())],
)
def nightly_etl():
    ...

Hooks are called with (flow, flow_run, state). Put them on the flow rather than on tasks, so one monitor tracks one deployment.

Or report around the work itself

@flow
def nightly_etl():
    ping("/start")
    try:
        rows = extract()
        load(rows)
    except Exception as exc:
        ping("/fail", str(exc).encode())
        raise
    ping("", f"loaded {len(rows)} rows".encode())

The start ping gives you the run's duration in Vivere, and the text body becomes the run log, so a late night's "what did it actually do" does not need the Prefect UI.

Monitor settings

Match the deployment's schedule: an interval schedule of 30 minutes is period 30m, a cron schedule goes in as a cron expression with the deployment's timezone. Prefect starts a run when a worker next polls, so add the poll interval to the grace on top of the flow's worst normal run.