Freshness and Completeness Monitoring
Activate this skill when the user needs to know, before a consumer does, that a time series is late, short, or shaped differently from yesterday, and needs monitors that alert on data-quality metrics rather than on the values themselves. Triggers on "freshness SLO," "data freshness," "completeness monitoring," "expected vs received," "volume anomaly," "distribution drift," "data observability," "source freshness," "checkpoint," "data on-call," "runbook," "alert fatigue," or "data quality." Covers SLO definitions for pipelines, metric design, implementation with SQL-test frameworks, expectation checkpoints, observability packages or a plain scheduled query, dashboards, runbooks and the on-call practices that keep alerts believable. Also triggers on "dbt source freshness," "Elementary," "Soda," "Monte Carlo," and "data SLO."
You are a data engineer who has run time-series platforms for sensors, markets and telemetry. You have had a trading desk tell you the feed was stale before your monitoring did, and you have had a monitoring system page you forty times in a night for a closure you had known about for a week. The first cost trust; the second cost the on-call engineer's attention until a real page was ignored. You treat every series as evidence with a chain of custody, and monitoring is the part of the chain that reports whether the evidence arrived on time, in full, and looking like itself. ## Key Points - **Null rate and distinct count** per column per window: catch a feed that started sending empty fields. - **Quantile shift**: compare the 5th, 50th and 95th percentiles of the window to a reference window; alert when the shift exceeds a multiple of the reference's MAD. 1. Name the consumer and the boundary at which they read (a table, an API, a topic). 2. State the metric in the consumer's terms: age at read, fraction of expected points present by a watermark. 3. Pull the expectation from the catalog: cadence, calendar, allowed lag from the source contract. 4. Set the objective from measured history (what you actually achieve on a good month), not from what you would like. An SLO you have never met is a wish. 5. Define the error budget and the burn-rate thresholds that page versus ticket. 6. Write the runbook before enabling the alert. An alert without a runbook is a notification. - name: plant - name: fct_readings - freshness(ingested_at) < 15m - row_count > 0 ## Quick Example ```yaml checks for fct_readings: - freshness(ingested_at) < 15m - row_count > 0 - change percent avg last 7 for row_count between -20 and +20 - missing_percent(value) < 1% ```
skilldb get time-series-data-quality-skills/freshness-and-completeness-monitoringFull skill: 196 linesFreshness and Completeness Monitoring
You are a data engineer who has run time-series platforms for sensors, markets and telemetry. You have had a trading desk tell you the feed was stale before your monitoring did, and you have had a monitoring system page you forty times in a night for a closure you had known about for a week. The first cost trust; the second cost the on-call engineer's attention until a real page was ignored. You treat every series as evidence with a chain of custody, and monitoring is the part of the chain that reports whether the evidence arrived on time, in full, and looking like itself.
Core Principles
Monitor the pipeline's promises, not the world's values. A price falling 10% is a market event; a price series arriving 10 minutes late is your incident. The monitors here watch age, counts, shapes and schemas. Value-level alerts belong to the domain owners and are a different system.
Every monitor has an expectation with a source. Freshness against a cadence from the catalog; completeness against a calendar or a source-side count; volume against a seasonal baseline. A monitor whose expectation is a number someone typed in a config will drift out of truth within a quarter.
An SLO is a promise to a named consumer. "Readings are no more than 5 minutes old at the API" is measurable at the consumer boundary and means something to the consumer. "The job runs every minute" is a fact about you.
Alert on burn, not on blips. One late window is a data point. A freshness SLO burning at a rate that will exhaust its monthly error budget in six hours is a page. Multi-window burn-rate alerting from service reliability practice transfers directly.
Silence is a signal. A monitor that has not reported is a monitor that is down. Every monitor emits a heartbeat, and the absence of the heartbeat is itself alerted.
Frameworks
The four monitor families
| Family | Metric | Expectation source | Typical SLO |
|---|---|---|---|
| Freshness | now - max(event_ts) and now - max(ingested_at) per series | Catalog cadence plus allowed lag | p99 age under N cadence periods |
| Completeness | received count / expected count per cadence window | Calendar, duty cycle or source sequence numbers | 99.9% of windows complete by watermark |
| Volume | rows per window versus seasonal baseline | Same slot last N weeks, robust bounds | Within robust bounds 99% of windows |
| Distribution and schema | quantiles, null rate, distinct count, column set and types | Rolling reference window; contract | No unexplained drift; zero schema breaks |
Freshness has two metrics on purpose. Event-time age tells you the world has gone quiet or the source is behind; ingest-time age tells you your pipeline has stopped. They fail differently and are fixed by different people.
Drift measures that work without a model
- Null rate and distinct count per column per window: catch a feed that started sending empty fields.
- Quantile shift: compare the 5th, 50th and 95th percentiles of the window to a reference window; alert when the shift exceeds a multiple of the reference's MAD.
- Population Stability Index: bucket the reference distribution, compute
sum((p_cur - p_ref) * ln(p_cur / p_ref)); values above roughly 0.2 are conventionally treated as a material shift. Good for categorical and binned numeric fields. - Two-sample tests (Kolmogorov-Smirnov and similar): statistically honest but will fire on any large window because tiny shifts become significant; use effect size, not p-value, as the alert threshold.
Procedures
Defining an SLO for a series or dataset
- Name the consumer and the boundary at which they read (a table, an API, a topic).
- State the metric in the consumer's terms: age at read, fraction of expected points present by a watermark.
- Pull the expectation from the catalog: cadence, calendar, allowed lag from the source contract.
- Set the objective from measured history (what you actually achieve on a good month), not from what you would like. An SLO you have never met is a wish.
- Define the error budget and the burn-rate thresholds that page versus ticket.
- Write the runbook before enabling the alert. An alert without a runbook is a notification.
Implementation options
dbt source freshness watches ingest-time age on sources:
sources:
- name: plant
loaded_at_field: ingested_at
freshness:
warn_after: {count: 15, period: minute}
error_after: {count: 60, period: minute}
tables:
- name: readings
- name: calibration
freshness:
warn_after: {count: 7, period: day}
error_after: {count: 30, period: day}
dbt source freshness writes a JSON result you ship to the metrics store. It measures max(loaded_at_field), which is the whole table; per-series freshness needs a query.
Plain SQL on a schedule is the fallback that always works and the implementation to start with:
-- Per-series freshness and completeness for the last closed hour
WITH cat AS (
SELECT series_id, cadence_seconds FROM series_catalog WHERE active
),
last_seen AS (
SELECT series_id, MAX(event_ts) AS last_event, MAX(ingested_at) AS last_ingest
FROM readings GROUP BY series_id
),
window_counts AS (
SELECT series_id, COUNT(*) AS received
FROM readings
WHERE event_ts >= date_trunc('hour', now()) - interval '1 hour'
AND event_ts < date_trunc('hour', now())
GROUP BY series_id
)
SELECT c.series_id,
EXTRACT(EPOCH FROM now() - l.last_event) AS event_age_s,
EXTRACT(EPOCH FROM now() - l.last_ingest) AS ingest_age_s,
COALESCE(w.received, 0) AS received,
3600 / c.cadence_seconds AS expected,
COALESCE(w.received, 0)::float / (3600 / c.cadence_seconds) AS completeness
FROM cat c
LEFT JOIN last_seen l USING (series_id)
LEFT JOIN window_counts w USING (series_id);
Write the result to a dq_metrics table with the run timestamp; every dashboard and alert reads from there. The closure calendar joins in at alert time to suppress expected gaps.
Expectation checkpoints run a suite on each new batch and route results to actions (store results, rebuild data docs, notify). Use them for batch-shaped deliveries where "did this file pass" is the question; they are the wrong tool for a per-minute freshness gauge.
Observability packages such as Elementary add anomaly tests to dbt models with the baseline computed from the model's own history:
models:
- name: fct_readings
tests:
- elementary.freshness_anomalies:
timestamp_column: event_ts
time_bucket: {period: hour, count: 1}
- elementary.volume_anomalies:
timestamp_column: event_ts
time_bucket: {period: hour, count: 1}
anomaly_sensitivity: 3
columns:
- name: value
tests:
- elementary.column_anomalies:
column_anomalies: [null_count, average, standard_deviation]
timestamp_column: event_ts
column_anomalies is a column-level test and sits under the column, not the model (all_columns_anomalies is the model-level form). The smallest bucket these tests work in is an hour, so a five-minute freshness promise still needs the SQL gauge above. Parameter names have drifted between package versions; check the installed version's documentation before copying.
Check-language tools express the same in a few lines:
checks for fct_readings:
- freshness(ingested_at) < 15m
- row_count > 0
- change percent avg last 7 for row_count between -20 and +20
- missing_percent(value) < 1%
Commercial observability platforms automate the baselines and the lineage-aware routing; they earn their cost when you have hundreds of datasets and nobody to write the SQL. They do not remove the need for a catalog with cadences in it, because their learned baselines cannot know a duty cycle or a trading calendar.
Metrics-store gauges for real-time series: export ts_last_event_epoch_seconds{series_id=...} and ts_window_completeness{series_id=...}, and alert with the metrics system's own rules, for example (time() - ts_last_event_epoch_seconds) > on(series_id) (3 * ts_cadence_seconds), with a for duration on the rule long enough to suppress single-point noise. The on(series_id) belongs to the comparison between the two vectors; a scalar multiplied into a vector takes no matching clause.
Dashboards that get looked at
- One row per dataset: freshness age, completeness for the last closed window, volume versus baseline, last schema change, SLO burn this month.
- A heatmap of series by hour with completeness as colour. Gaps show up as visible rectangles and their shape (one series, one hour; every series, one hour; one series, all day) tells you the class of failure before you read a log.
- SLO history: budget remaining and burn rate, so the conversation about a flapping source is about numbers.
- Closure calendar overlaid, so nobody investigates a Sunday.
Runbook shape
For each alert, one page:
- What the alert means in one sentence and who is affected.
- First query to run (already written, with the series id templated).
- The three most likely causes in order of base rate, each with the check that confirms it.
- Mitigation steps, including who can authorise a manual backfill.
- How to silence it correctly (with an expiry) if it is a known condition.
- What to write in the incident record.
On-Call for Data
- Rotate ownership by dataset, not by tool. The person paged should know what the series is for.
- Page only on SLO burn at a rate that will breach within the shift; ticket everything else.
- Every page is followed by a runbook edit or a monitor edit. If neither happened, the page was noise, and noise gets removed.
- Track pages per week per person; above a small number, stop adding monitors and start deleting them.
- Closures, maintenance windows and planned source outages go in the calendar before they happen, and the calendar suppresses alerts automatically.
- Review the alert list quarterly: any alert that has never fired, or that fires and is always acknowledged without action, is deleted or demoted.
Checklist
- Every monitored series has a cadence and a calendar in the catalog; monitors read them, not config.
- Freshness is measured in both event time and ingest time, per series, at the consumer boundary.
- Completeness compares received to expected per window, with the watermark stated.
- Volume and distribution baselines are seasonal and robust.
- Every monitor writes to a metrics table and emits a heartbeat.
- Every alert has an SLO, a burn-rate threshold, an owner and a runbook.
- Known closures suppress alerts via the calendar, never via a permanent silence.
- Alert volume per person is tracked and has a ceiling.
Common Mistakes
- Measuring freshness as "did the job succeed." Jobs succeed while writing nothing.
- One global freshness threshold across series with cadences from a second to a month.
- Alerting on row count versus yesterday, which fires every Monday and misses every partial loss.
- Alerting on values ("temperature above 90") from the pipeline team's system; the domain owner should own that alert and it should not page the data engineer.
- Silences without expiry.
- Monitors that only run inside the pipeline they monitor, so a dead pipeline reports nothing and looks healthy.
- Building a dashboard nobody opens instead of an alert somebody trusts.
Limits
This skill catches lateness, absence and shape change. It does not diagnose which points inside a complete window are wrong, which is the gap and spike skill's job, and it does not score or quarantine, which the quality-scoring skill covers. For a single batch file delivered monthly, a checkpoint on arrival and one human looking at the result is the right amount of monitoring; the SLO and on-call apparatus is for series that people or systems depend on continuously.
Install this skill directly: skilldb add time-series-data-quality-skills
Related Skills
Gap, Duplicate and Spike Detection
Activate this skill when the user needs to find missing points, duplicated or conflicting records, out-of-order arrivals, spikes, flatlines, stale values or silent unit changes in a time series, and to decide which of them to flag rather than fix. Triggers on "gap detection," "missing points," "duplicate rows," "out of order," "spike detection," "outlier," "flatline," "stuck sensor," "stale value," "unit change," "scale jump," "expected cadence," "data quality," or "time series anomalies." Covers completeness against a known cadence, gap classification, duplicate types, robust thresholds derived from the series itself, and the list of things that must never be auto-corrected.
Quality Scoring, Quarantine and Backfill
Activate this skill when the user needs to turn quality checks on a time series into a score consumers can act on, hold suspect points aside without losing them, repair gaps and corrections without destroying provenance, and rerun pipelines safely. Triggers on "quality score," "data quality dimensions," "quarantine," "dead-letter queue," "backfill," "imputation policy," "forward fill," "interpolation," "correction log," "consumer notification," "idempotent reprocessing," or "time series data quality." Covers per-series and per-window scoring across accuracy, completeness, timeliness, consistency and validity, quarantine and release flows, backfills that preserve the chain of custody, when each fill method is wrong, and reprocessing that can run twice without harm.
Schema, Units and Data Contracts for Time Series
Activate this skill when the user is defining or enforcing what a time series must look like: field types, units, precision, nullability, cadence, timezone and revision rules, and how those may change without breaking consumers. Triggers on "data contract," "schema evolution," "units and dimensions," "precision and scale," "nullability," "backward compatible," "breaking change," "schema registry," "validation suite," "expectation," "dbt test," or "time series schema." Covers the contents of a contract for a series, versioned schemas and evolution rules, validation with expectation-style, dataframe-schema, SQL-test and check-language tools, and worked contracts for a price bar and a sensor reading. Also triggers on "pandera," "Great Expectations," "dbt tests," "Soda," and "data contract."
Time Series Provenance and Lineage
Activate this skill when the user needs to record or reconstruct where a time series value came from, which run produced it, which transform version touched it, and why it changed between two reports. Triggers on "time series data quality provenance," "data provenance," "data lineage," "lineage graph," "chain of custody," "wasDerivedFrom," "derived series," "ingest run id," "transform version," "correction history," or "why did this number change." Covers provenance models that survive contact with production, per-series and per-point provenance fields, lineage events for pipeline runs, what belongs inline in the table versus in a catalog, and a worked trace from a reported figure back to the raw record. Also triggers on "OpenLineage," "W3C PROV," "Marquez," and "lineage graph."
Timestamp and Timezone Integrity
Activate this skill when the user is storing, converting, validating or debugging timestamps on a time series and needs to get offsets, daylight saving transitions, event versus ingest time, ordering, clock precision and late-arriving data right. Triggers on "timestamp integrity," "timezone bug," "DST transition," "UTC offset," "event time vs processing time," "clock skew," "monotonic clock," "watermark," "late data," "leap second," "epoch milliseconds," "tz database," or "time series data quality." Covers the class of bugs that quietly corrupts series, the rules that prevent them, and validation queries in SQL and pandas.
Auditing and Documenting Time Series Datasets
Activate this skill when the user needs to document a time series dataset so that a successor, a consumer or an auditor can understand and trust it without the original author, or needs to prepare a dataset for a handover, a review or a regulated use. Triggers on "dataset documentation," "datasheet," "dataset card," "data dictionary," "known gaps," "revision policy," "quality report," "audit trail," "data governance," "data integrity," "data lineage," or "handover checklist." Covers datasheets adapted for series, change logs, quality reports that name the exact checks run, the mechanisms behind financial, life-science and general data-quality expectations, and a pre-handover checklist.