Schedule icon
Webhook icon
Request icon
Script icon
Query icon
If icon
SlackIncomingWebhook icon

Metabase Cache and Consistency Ops Guard

Compare Metabase dashboard numbers with warehouse KPI rows in Kestra, alert Slack on row or revenue drift, and re-warm stale card caches.

Categories
BusinessData

Dashboards lie quietly. A stale cache, a dropped filter, or a late warehouse load can leave a Metabase dashboard showing numbers that nobody has checked. This flow reconciles the dashboard against the warehouse on a schedule or on demand, tells Slack exactly what disagreed, and kicks a cache warming retry when freshness is the problem.

How it works

  1. The hourly_consistency_check trigger (io.kestra.plugin.core.trigger.Schedule, shipped with disabled: true) and the on_demand_check trigger (io.kestra.plugin.core.trigger.Webhook) give you a six hour cadence and an on demand HTTP entry point.
  2. fetch_dashboard (io.kestra.plugin.core.http.Request) GETs /api/dashboard/{id} with an X-API-KEY header sourced from the METABASE_API_KEY secret, and pick_primary_card (io.kestra.plugin.scripts.python.Script) reads the definition to select the first card.
  3. fetch_card_query POSTs /api/card/{card_id}/query, so the run compares against the row count, revenue total, and data timestamp that the dashboard itself is serving.
  4. load_warehouse_kpi (io.kestra.plugin.jdbc.postgresql.Query) runs the matching aggregate over public.fact_orders with fetchType: STORE.
  5. check_consistency (Python) parses both payloads and emits a ::json:: verdict carrying both row counts, both revenue totals, the delta percent, staleness minutes, and a sanitized issue summary.
  6. route_verdict (io.kestra.plugin.core.flow.If) branches on that verdict, with alert_drift and warm_cache_retry in the then branch and all_clear_note in the else branch, while the errors block reports any crash to Slack.

What you get

  • A verdict output (ok, drift, stale, unavailable) that any downstream flow can branch on.
  • Numbers in the Slack alert, so the channel sees both row counts, both revenue totals, the delta percent, and staleness in a single message.
  • A cache warming retry that re-runs the card query when the numbers are old, instead of leaving viewers on stale results.
  • An execution history that records every comparison, which turns "did anyone check the dashboard" into an answerable question.

Who it's for

  • Analytics engineers who own a Metabase dashboard and need evidence that it matches the warehouse.
  • Data platform teams that run freshness and reconciliation checks next to their ELT pipelines.
  • BI operations leads who want a Slack signal before stakeholders spot a wrong number.

Why orchestrate this with Kestra

A one off script can call two APIs, but it cannot retry a flaky Metabase request, branch on a verdict, or leave an audit trail behind every comparison. Kestra puts the schedule, the webhook, the secrets, the branch, and the notifications in one versioned file, so the guard is reviewed like any other change. The If branch keeps a healthy run quiet and an unhealthy run loud, the errors handler catches crashes in the guard itself, and every execution stores the exact numbers that were compared.

Prerequisites

  • A Metabase instance with an API key and a dashboard whose first card returns row_count, revenue_total, and updated_at, or adapt the two queries to your column names.
  • A Postgres warehouse with a public.fact_orders table holding order_total and updated_at.
  • Network access from the Kestra worker to Metabase, to Postgres, and to Slack.
  • Python 3 available on the Kestra worker for the two script steps.

Secrets

  • METABASE_API_KEY: Metabase API key sent as the X-API-KEY header.
  • POSTGRES_JDBC_URL, POSTGRES_USERNAME, POSTGRES_PASSWORD: warehouse connection details used by the KPI query.
  • SLACK_WEBHOOK_URL: incoming webhook for the alert, the all clear note, and the failure message.
  • METABASE_GUARD_WEBHOOK_KEY: key segment of the on demand webhook URL.

Quick start

  1. Add the five secrets above to your Kestra namespace.
  2. Set metabase_url and dashboard_id, then choose a staleness_threshold_minutes that matches your load cadence.
  3. Run the flow once by hand and read the verdict output before trusting the branch.
  4. Enable the schedule by flipping the trigger, for example:
triggers:
  - id: hourly_consistency_check
    type: io.kestra.plugin.core.trigger.Schedule
    cron: "0 */6 * * *"
    disabled: false
  1. Or call the webhook directly from a shell:
curl -X POST https://<your-kestra-host>/api/v1/executions/webhook/company.team/metabase-dashboard-ops-guard/<METABASE_GUARD_WEBHOOK_KEY>

How to extend

  • Point the warehouse query at a different table or add more KPI columns, then map the new fields inside check_consistency.
  • Alert a second channel by adding another Slack task to the then branch, or route the verdict through a subflow.
  • Replace all_clear_note with io.kestra.plugin.core.log.Log when a clean run should stay silent in Slack.
  • Add io.kestra.plugin.core.execution.Fail after alert_drift when a drift verdict should turn the whole execution red for downstream gates.

Links

See How

New to Kestra?

Use blueprints to kickstart your first workflows.