If icon
ReadRows icon
OutputValues icon
Return icon
Switch icon
Log icon
SlackIncomingWebhook icon
Schedule icon

Bigtable Column Family Completeness Gate

Audit Bigtable rows for missing required column families on a schedule and alert Slack with the exact row keys affected.

Categories
DataInfrastructure

Bigtable has no schema to enforce, a column family exists the moment any cell is written to it, and nothing stops a buggy write path, a half-finished migration, or a dropped field in an application update from silently producing rows that are missing a family every downstream reader assumes is there. The table itself will never tell you, a read for a missing family just comes back empty, no error. This blueprint turns a row sample into a Day 2 compliance gate: it reads a batch of real rows, checks each one against the column families it is supposed to have, and alerts Slack with the exact row keys and families missing, never a guess.

How it works

  1. route_connection_mode (io.kestra.plugin.core.flow.If) picks the connection based on use_emulator. probe_rows_emulator connects to a local Bigtable emulator via emulatorHost with no credentials, probe_rows_live connects to real Bigtable with a GCP service account. Both run io.kestra.plugin.gcp.bigtable.ReadRows with rowKeyPrefix, limit: sample_size, and fetchType: FETCH, and both have allowFailure: true so an unreachable instance or missing table does not stop the flow before it can be classified as PROBE_FAILED below.
  2. audit_column_families (io.kestra.plugin.core.output.OutputValues) reads whichever branch's rows list exists (guarded with is defined/is not defined, since exactly one of the two probe tasks ran), computes the distinct cells[].family values present on each row, and flags every row missing a required family per column_family_check_mode.
  3. classify_compliance_status (io.kestra.plugin.core.debug.Return) returns PROBE_FAILED when neither probe produced output, CRITICAL or WARNING depending on whether the violation count meets violation_count_critical_threshold, or COMPLIANT when there are none.
  4. route_by_compliance_status (io.kestra.plugin.core.flow.Switch) branches on that status: COMPLIANT logs it, WARNING, CRITICAL, and PROBE_FAILED each alert Slack with the exact row keys and missing families from the audit, never an invented count.
  5. log_audit_summary always runs last and logs the final classification.
  6. The errors block posts to Slack if the flow itself fails outside the gate logic above.

What you get

  • A real answer to whether sampled rows actually have the shape your downstream readers assume, not a hope that every write path stayed in sync.
  • A Slack alert naming the exact row keys and the exact missing families, never an invented number.
  • A fully local test path: the default inputs point at a Bigtable emulator, so the whole gate can be exercised without a GCP account.

Who it's for

  • Data platform teams running Bigtable for time-series, IoT, or event data who need to catch a silently broken write path before a downstream job fails on a missing field.
  • Teams migrating or reshaping a Bigtable schema who want proof that every row in a namespace already has the new column family before cutting over readers to depend on it.
  • Teams evaluating the Bigtable plugin who want a working example of io.kestra.plugin.gcp.bigtable.ReadRows against both the local emulator and real Bigtable, using the same task with different connection properties.

Why orchestrate this with Kestra

Bigtable itself enforces nothing about which families a row contains, there is no constraint, no alert, no concept of "this row looks wrong". Kestra adds the recurring sample, the column family completeness check, and a Slack alert built from the rows' own data, all as one declarative YAML file you can version and review like code, runnable against a local emulator before it ever touches a real table.

Prerequisites

  • For local testing, the official Bigtable emulator, no GCP account needed:

    docker run -p 127.0.0.1:8086:8086 --rm -ti google/cloud-sdk gcloud beta emulators bigtable start --host-port=0.0.0.0:8086
    

    Port 8086 does not collide with Kestra's own default UI port. The emulator supports table creation and read/write against any project and instance name, it does not support the full instance/cluster administrative API, which this blueprint does not use.

  • For real Bigtable, a GCP project with a Bigtable instance and table, and a service account with bigtable.rows.readColumnFamilies-equivalent read permission (roles/bigtable.reader is enough, this blueprint only reads).

  • A Slack incoming webhook for alerts.

  • This plugin calls the Bigtable Data API directly from the Kestra worker process, there is no sub-container taskRunner. If Kestra itself runs in a container and use_emulator is true, point emulator_host at host.docker.internal:8086 or connect Kestra's container to the emulator's Docker network and use its container name instead.

Secrets

  • GCP_PROJECT_ID, GCP_SERVICE_ACCOUNT: used by probe_rows_live only, not needed when use_emulator is true.

  • SLACK_WEBHOOK_URL: Slack incoming webhook URL used by the alert tasks and the errors block.

    In Kestra OSS, set all three as base64-encoded environment variables: SECRET_GCP_PROJECT_ID, SECRET_GCP_SERVICE_ACCOUNT, SECRET_SLACK_WEBHOOK_URL.

Inputs

Input Type Default Allowed values
instance_id STRING demo-instance any Bigtable instance ID
table_id STRING sensor-readings any Bigtable table ID
row_key_prefix STRING `` (empty) any row key prefix, empty scans the whole table
required_column_families JSON ["readings", "metadata"] JSON array of column family names
column_family_check_mode SELECT any_required_missing any_required_missing, all_required_missing
sample_size INT 100 any positive integer
violation_count_critical_threshold INT 10 any non-negative integer
use_emulator BOOL true true, false
emulator_host STRING localhost:8086 any host:port

Quick start

  1. Start the Bigtable emulator, create a table and write a few rows with the cbt CLI or the Bigtable client of your choice, some with every required family, some without.
  2. Add the SLACK_WEBHOOK secret.
  3. Run this flow manually with the defaults and read log_audit_summary for the result.
  4. Set use_emulator: false and add the GCP_PROJECT_ID and GCP_SERVICE_ACCOUNT secrets to point this at a real table.
  5. Enable the hourly trigger.

How to extend

  • Narrow row_key_prefix to the namespace of a single tenant or device and run one instance of this flow per tenant with different required_column_families, if different tenants are expected to have different schemas.
  • Add a qualifier-level check inside a required family, not just family presence, for example requiring a specific column like metadata:schema_version to exist.
  • Chain a downstream task after CRITICAL that writes the exact violating row keys to a file in internal storage for a data engineer to inspect, instead of only Slack.

Pitfalls

  • sample_size is a sample, not a full table scan. A rare violation pattern can be missed entirely if it falls outside the sampled rows, raise sample_size or scope row_key_prefix tighter for a more complete audit.
  • A row with zero cells at all (never written, or fully deleted) has no families and is correctly flagged a violation under both column_family_check_mode settings, that is expected, not a false positive.
  • The Bigtable emulator does not persist data across container restarts by default and does not support the full administrative API, do not rely on it for anything beyond local testing of this gate's logic.
  • emulatorHost uses plaintext transport with no credentials, only ever point it at a local emulator, never at a real endpoint.

Links

See How

New to Kestra?

Use blueprints to kickstart your first workflows.