Uptime and history
How raw results become daily summaries, uptime percentages and the public snapshot.
A status page answers two questions: what is happening now, and what has been happening. Heyo Status keeps raw observations for the recent past, compacts older ones into daily rows, and derives everything a page shows from that one query.
Two tables
| Table | Row meaning | Written by |
|---|---|---|
status_results | one check of one monitor | every check run |
status_daily_summaries | one monitor-day: totals, downtime, latency, latest | the summarize job |
Compaction (summaryCron, default 10 0 * * *) reads every raw result before
the start of today, writes one summary row per monitor per day with
ON CONFLICT … DO UPDATE, and deletes the raw rows it consumed. Re-running it
is safe.
Cleanup (cleanupCron, default 0 3 * * *) deletes raw results older than
retentionDays and summaries older than the same boundary.
When a day has both a summary and raw rows — a retried compaction, say — the summary is authoritative and the raw rows for that day are ignored. That is what keeps a compaction retry from double counting.
Building a snapshot
getStatusSnapshot(config, now?) reads three things in parallel: history for
every public monitor over displayDays, active incidents, and upcoming
maintenance windows. Then it derives:
Exactly displayDays entries, oldest first, one per UTC day. Each carries
a status (up, down, degraded, empty), a tooltip, the check and
failure counts, downtime in minutes and a down percentage.
Uptime across the whole displayed window. null when no checks exist —
never 0, which would read as a total outage.
Period uptime. Raw rows are filtered by exact timestamp; daily summaries cannot be split, so their whole calendar day is included.
Hourly averages over the last 24 hours while raw rows exist, otherwise daily averages over the last 7 days. Empty buckets are omitted, not reported as zero latency.
The newest observation, preferring a raw row over a summary when it is newer.
Mean latency across every check in the window.
A day with no checks is empty, not down. A day where every check failed is
down; a day with some failures is degraded.
Downtime in minutes
Each failing raw result is attributed the time until the next observation,
clamped to the end of its UTC day and to now. The sum is rounded up to whole
minutes. This gives a defensible "minutes down" figure from sampled data
without pretending the sampling was continuous.
Overall and group status
Resolved in strict order:
- An
in-progressmaintenance window →maintenance - An active
criticalincident →major-outage - Any other active incident →
degraded - Every monitor failing →
major-outage - Some monitors failing →
degraded - Otherwise →
operational
Group status is simpler: operational with no failures, outage when every
monitor in the group is failing, degraded in between.
The snapshot payload
hasConfig is false when no public monitor is configured, and hasData is
false until the first check lands. Themes use them to show an onboarding
state instead of an empty grid.
Private monitors are filtered out before any of this runs, so they cannot appear in a group, a percentage or a metric.
Reading it
Pass an explicit Date to bypass the cache and get a deterministic result:
To build a snapshot from history you loaded yourself — a test, a migration, an export — use the pure function:
Seeding history
A brand new page has an empty grid. The CLI can fabricate deterministic, evenly distributed history — 100 checks per monitor per day — and compact it immediately: