New to Kestra?
Use blueprints to kickstart your first workflows.
Detect a stalled Bigtable writer by reading the newest cell timestamp under a row key and alert Slack before data goes stale.
A device, sensor, or ingestion pipeline that writes continuously into a single Bigtable row can stop silently - Bigtable itself has no concept of "this row should be updated every few minutes and has not been." This blueprint reads the single most recent cell version under a row key prefix and alerts the moment it falls behind an acceptable staleness window, or is missing outright.
periodic_freshness_check (io.kestra.plugin.core.trigger.Schedule) runs every 15 minutes, explicitly passing gcp_project_id, bigtable_instance_id, bigtable_table_id, row_key_prefix, max_staleness_minutes, and bigtable_emulator_host through its own inputs: block so the flow is self-contained when the trigger fires. Shipped disabled so a manual run can be verified first.probe_latest_row (io.kestra.plugin.gcp.bigtable.ReadRows, fetchType: FETCH_ONE) reads the single row matching row_key_prefix, returning row.cells[0] as the most recently written cell - Bigtable's own documented behavior returns cell versions newest-first within a row.evaluate_row_presence (io.kestra.plugin.core.flow.If) branches on whether any row matched at all.else (NO_MATCHING_ROW): alert_no_matching_row reports that the prefix matched nothing.then (row found): nested evaluate_staleness (io.kestra.plugin.core.flow.If) branches on whether the latest cell's timestamp is older than max_staleness_minutes.then (STALE): alert_stale_row reports the last-write time to Slack.else (FRESH): log_row_fresh records a clean check.log_audit_complete always runs last, printing whether a row was found regardless of which branch fired.errors block alerts Slack separately if the flow itself fails outright - an unreachable Bigtable instance or bad credentials must not read as any of the three handled states.emulatorHost - verified from the plugin's own source to use NoCredentialsProvider when set, unlike this repository's GCP Pub/Sub and Firestore blueprints, which both need their emulator variable set on the Kestra worker process itself rather than inside the flow.io.kestra.plugin.gcp.bigtable.ReadRows used for a single-row freshness probe rather than a bulk data export.ReadRows's emulatorHost property, since every example in the plugin's own documentation targets real Bigtable.Bigtable itself has no scheduler and no "alert me if this row goes stale" primitive - checking requires either instrumenting every writer with its own heartbeat logic or someone remembering to query the row by hand. Kestra supplies the schedule, the FRESH/STALE/NO_MATCHING_ROW classification as a first-class branch, and an execution history that shows exactly when a writer stalled and for how long.
bigtable_instance_id, bigtable_table_id, row_key_prefix), or a local emulator for testing.roles/bigtable.reader - not needed at all when using bigtable_emulator_host.Local testing (official Bigtable emulator, no GCP account needed):
docker run -d --name bigtable-emulator -p 8086:8086 google/cloud-sdk gcloud beta emulators bigtable start --host-port=0.0.0.0:8086
docker network connect YOUR_KESTRA_NETWORK bigtable-emulator
cbt -project emulator-test -instance my-instance createtable sensor-readings
cbt -project emulator-test -instance my-instance createfamily sensor-readings cf1
cbt -project emulator-test -instance my-instance set sensor-readings "device-42#001" cf1:reading=72
Run docker network ls first to find the active Kestra network name; cbt is the Bigtable CLI bundled with the Cloud SDK, used here against the emulator with BIGTABLE_EMULATOR_HOST=localhost:8086 exported first. From inside the Kestra worker's Docker network, set bigtable_emulator_host to bigtable-emulator:8086 instead of localhost:8086.
GCP_SERVICE_ACCOUNT_JSON: service account key used by io.kestra.plugin.gcp.bigtable.ReadRows against real Bigtable. Can be left as any placeholder value when bigtable_emulator_host is set, since the emulator path never reads it.SLACK_WEBHOOK_URL: Slack incoming webhook used by alert_stale_row, alert_no_matching_row, and the errors block.SECRET_, base64-encoded - for example SECRET_SLACK_WEBHOOK_URL=$(echo -n '<your-webhook-url>' | base64) - and read back in flows with {{ secret('NAME') }}.| Input | Type | Default | Allowed values |
|---|---|---|---|
gcp_project_id |
STRING | <your-gcp-project-id> |
Valid GCP project ID (ignored when emulatorHost is set) |
bigtable_instance_id |
STRING | my-instance |
Bigtable instance ID |
bigtable_table_id |
STRING | sensor-readings |
Bigtable table ID |
row_key_prefix |
STRING | device-42# |
Row key prefix resolving to a single row |
max_staleness_minutes |
INT | 30 |
Any positive integer |
bigtable_emulator_host |
STRING | "" |
host:port for local emulator testing, or empty for production |
{{ outputs.probe_latest_row.row }}: the matched row map (rowKey and cells list with microsecond timestamp); null when no row matched.{{ outputs.probe_latest_row.rowCount }}: 1 when a row was found, 0 otherwise.SLACK_WEBHOOK_URL secret (GCP_SERVICE_ACCOUNT_JSON can stay a placeholder).bigtable_emulator_host: "localhost:8086" against the just-written row and confirm log_row_fresh fires.max_staleness_minutes (or lower it for the test) without writing again, then re-run to confirm alert_stale_row fires.row_key_prefix at a prefix with no matching row and re-run to confirm alert_no_matching_row fires.bigtable_emulator_host and point this at a real Bigtable instance once you trust the check; enable periodic_freshness_check once you trust the cadence.row_key_prefix values with io.kestra.plugin.core.flow.Loop to freshness-check every monitored device or entity in one run.cells[1], cells[2], and so on (older versions of the same cell, still newest-first) to additionally detect a writer that is updating on a slower cadence than normal, not just one that stopped outright.columnFamily filter if the table carries multiple column families and only one represents the writer's own heartbeat data.row_key_prefix must narrow to exactly one row for this freshness check to mean anything. fetchType: FETCH_ONE returns the row with the lexicographically smallest key matching the prefix, not the most recently written row across multiple matches - if the prefix matches several rows, this blueprint silently checks only one of them.evaluate_staleness divides by 1,000,000 before converting to a date for exactly that reason; dividing by 1,000 as if it were milliseconds would make every row look roughly 1,000x more stale than it actually is.emulatorHost is explicitly documented as "not intended for production use" by the plugin itself - this blueprint's bigtable_emulator_host input must be cleared before pointing the flow at a real account.row.cells[0] throw while rendering, which surfaces through the errors block rather than a silent misclassification.ReadRows properties and output shape, the emulatorHost no-credentials behavior, and the microsecond timestamp unit are all taken from the plugin's own source on main and Cloud Bigtable's own client library documentation - but no Docker container or Kestra engine was run to execute this flow end to end.