AccelerUpDocs
Data warehouse/SQL alerts/Recipes

SQL alert recipes

Working queries for reconciliation, drift, freshness, spikes and per-subject volume.

Real queries, in the shape AccelerUp expects. Adapt the table names.

Freshness — is the mirror still replicating?

The single most valuable rule to have, because it makes every other rule trustworthy.

sql
SELECT
    dateDiff('minute', max(_ingested_at), now()) AS mirror_lag_min
FROM warehouse.orders

Condition: mirror_lag_min outside 1 … 60, severity high.

A lag of 0 is as suspicious as one of 900 — it usually means the clock or the query is wrong, not that replication is perfect. A corridor catches both.

Mark this condition a guard on any rule that reads the same table, and a stale mirror will report "the data is 4 hours old" rather than "failures dropped to zero".

Reconciliation — do two systems agree?

sql
SELECT
    concat('recon/', toString(day))     AS subject,
    ours,
    theirs,
    abs(ours - theirs)                  AS gap,
    round(100.0 * abs(ours - theirs) / nullIf(theirs, 0), 3) AS gap_pct
FROM (
    SELECT
        toDate(created_at)                       AS day,
        countIf(source = 'internal')             AS ours,
        countIf(source = 'provider')             AS theirs
    FROM warehouse.payment_events
    WHERE created_at > now() - INTERVAL 3 DAY
    GROUP BY day
)

Group by subject. Condition: gap_pct gt 0.1, warn at 0.05.

Keys rotate daily — AccelerUp resolves a firing subject that stops appearing, so yesterday's alert does not stay open forever.

Drift — is volume where it should be by now?

sql
SELECT
    subject,
    formatDateTime(toStartOfDay(now()), '%Y-%m-%d 00:00-%H:00') AS measured_window,
    actual_so_far,
    expected_so_far,
    round(100.0 * actual_so_far / nullIf(expected_so_far, 0), 1) AS pct_of_expected
FROM warehouse.volume_expectation

Group by subject. Condition: actual_so_far, mode ratio of expected_so_far, lt 70, unit %.

The alert then says "412 against an expected 900 over 2026-09-14 00:00-10:00" — both numbers and the window, because a percentage alone sends the reader to a dashboard before they can judge it.

Spike or drop, decided per row

sql
SELECT
    segment                                                   AS subject,
    drift_sigma,
    multiIf(observed > expected, 'spike',
            observed < expected, 'drop',
            'flat')                                           AS anomaly_kind,
    formatDateTime(now(), '%Y-%m-%d %H:00')                   AS measured_window
FROM warehouse.segment_drift
WHERE abs(drift_sigma) > 1

Group by subject. Condition: drift_sigma with direction column anomaly_kind:

DirectionCondition
spikegt 2.5, severity high
droplt -2.5, severity critical
flatnot alertable

See Directions.

Error rate with a volume guard

A percentage computed from three requests is not a percentage.

sql
SELECT
    count()                                                AS requests,   -- guard
    countIf(status >= 500)                                 AS errors,
    round(100.0 * countIf(status >= 500) / count(), 2)     AS error_pct
FROM warehouse.http_events
WHERE ts > now() - INTERVAL 15 MINUTE

Conditions:

ColumnConditionKind
requestsgte 100guard
error_pctgt 2, warn at 1measurement

Below a hundred requests the guard fails, the measurement is not reported, and nobody is paged about a 33% error rate from one failed health check.

Per-subject volume, rolled up into one message

sql
SELECT
    subject,
    count() AS volume
FROM warehouse.mail_message_logs
WHERE created_at > now() - INTERVAL 1 HOUR
GROUP BY subject
HAVING volume > 0

Group by subject, notify mode summary, condition volume lt 50.

Forty subjects breaching produce one message listing them, while each keeps its own state and recovers independently. See Notifications.

Batch-window rule

A reconciliation that only means anything after the nightly load:

  • Run every 30 minutes
  • Run at on the clock, offset 15
  • Run window 03:00 → 07:00, Asia/Baku

The query is not run outside the window at all — so the warehouse is not asked three hundred pointless questions a day, and the rule cannot fire at 04:10 against a half-loaded table. See Schedules.

Part of Data warehouseQuerying the warehouse, and alerting on what the query returns.