Event contract
Fields the query expects
| Field | Type | Why it exists |
|---|---|---|
| timestamp_utc | Timestamp | Observed cron-run start time. |
| schedule_name | Utf8 | Stable scheduled-workflow name. |
| environment | Utf8 | Deployment environment. |
| expected_interval_minutes | Float64 | Documented minutes between starts. |
Copy the query
WITH ordered_runs AS (
SELECT
schedule_name,
environment,
timestamp_utc AS run_at,
expected_interval_minutes,
LAG(timestamp_utc) OVER (
PARTITION BY schedule_name, environment
ORDER BY timestamp_utc
) AS previous_run_at
FROM cron_runs
WHERE timestamp_utc >= now() - INTERVAL '7 days'
)
SELECT
schedule_name,
environment,
previous_run_at,
run_at,
expected_interval_minutes,
date_part('second', run_at - previous_run_at) / 60.0 AS observed_gap_minutes
FROM ordered_runs
WHERE previous_run_at IS NOT NULL
AND date_part('second', run_at - previous_run_at) / 60.0
> expected_interval_minutes * 1.5
ORDER BY observed_gap_minutes DESC;This read-only query is planned and executed against an empty typed table with Apache DataFusion 45.2.0. The deterministic sample output is synthetic and reviewed separately; validate field types, thresholds, and business definitions against your own data. Read the testing methodology.
Query result
Observed schedule gaps
The billing sync missed two expected starts, while the daily expiration workflow skipped one day.
| schedule_name | environment | previous_run_at | run_at | expected_interval_minutes | observed_gap_minutes |
|---|---|---|---|---|---|
| hourly_billing_sync | production | 2026-07-27 07:00 | 2026-07-27 10:00 | 60 | 180 |
| daily_trial_expiration | production | 2026-07-25 02:00 | 2026-07-27 02:00 | 1,440 | 2,880 |
Synthetic example output. Run the query against your own event schema and thresholds before using it for operational decisions.
How the SQL works
- 1LAG retrieves the previous observed start for each schedule and environment.
- 2The timestamp difference becomes an observed gap that can be compared with each job's own interval.
- 3A 1.5 multiplier allows normal scheduling jitter without hiding a genuinely missed run.
Edge cases to decide
- This historical query detects a gap only after the next run arrives; pair it with a current missing-heartbeat alert.
- Planned maintenance and disabled schedules should emit explicit state changes.
- Daylight-saving transitions require UTC schedules or timezone-aware expectations.
Recommended dashboard
- Table: missed starts with previous and next run
- Trend: schedule-gap minutes by job
- Stat: production schedules currently overdue
Alert guidance
For real-time detection, alert when the latest run is older than the expected interval plus documented jitter.
Read alert setupPut the recipe to work
Related instrumentation and guides
Continue the analysis
Measure Background Job Retry and Failure Rate
Find unreliable jobs by comparing successful runs, retries, failures, and tail duration.
Open recipeFind Stalled Background Jobs With SQL
Join job start and finish events to identify work that exceeded its expected completion window.
Open recipeDetect Missing Service Heartbeats
Find services, workers, or scheduled tasks that stopped reporting before a failure event appeared.
Open recipeRun it on real events
Create a table, adapt the fields, and save the result
Start free, send structured events, and use the query result as a chart, shared dashboard widget, or alert input.