id: clickhouse-automated-backup-and-disaster-recovery-sentinel
namespace: company.database
inputs:
- id: clickhouse_host
type: STRING
displayName: ClickHouse Host
description: Hostname or IP address of the target ClickHouse server or cluster proxy.
defaults: "localhost"
- id: clickhouse_port
type: INT
displayName: ClickHouse Native/HTTP Port
description: TCP or HTTP port for the ClickHouse JDBC driver (default 8123 for
HTTP protocol).
defaults: 8123
- id: database_name
type: STRING
displayName: Default Database
description: ClickHouse database context used when establishing the initial connection.
defaults: "system"
- id: clickhouse_username
type: STRING
displayName: ClickHouse Username
description: Database user with SELECT permissions on system.backups catalog.
defaults: "default"
- id: lookback_days
type: INT
displayName: Lookback Window (Days)
description: Historical period in days over which automated backup jobs are audited.
defaults: 7
- id: max_stale_backup_hours
type: INT
displayName: Maximum Backup Staleness (Hours)
description: Maximum allowable hours elapsed since the latest successful backup
before raising an SLA breach alert.
defaults: 26
triggers:
- id: daily_backup_audit_schedule
type: io.kestra.plugin.core.trigger.Schedule
description: Automated daily audit scheduled at 04:00 UTC to verify completion
of nightly ClickHouse backup pipelines.
cron: "0 4 * * *"
disabled: true
tasks:
- id: probe_backup_telemetry
type: io.kestra.plugin.jdbc.clickhouse.Query
description: Execute analytical query against ClickHouse system.backups to
evaluate backup successes, failures, and execution recency.
url: "jdbc:clickhouse://{{ inputs.clickhouse_host }}:{{ inputs.clickhouse_port
}}/{{ inputs.database_name }}"
username: "{{ inputs.clickhouse_username }}"
password: "{{ secret('CLICKHOUSE_PASSWORD') }}"
fetchType: FETCH_ONE
allowFailure: true
sql: |
SELECT
countIf(status NOT IN ('BACKUP_CREATED', 'CREATING_BACKUP') OR error != '') AS failed_backups_count,
countIf(status = 'BACKUP_CREATED') AS successful_backups_count,
count() AS total_backups_evaluated,
argMax(name, start_time) AS latest_backup_name,
argMax(status, start_time) AS latest_backup_status,
argMax(error, start_time) AS latest_backup_error,
argMax(total_size, start_time) AS latest_backup_size_bytes,
argMax(num_files, start_time) AS latest_backup_files,
max(start_time) AS latest_backup_start_time,
dateDiff('hour', max(start_time), now()) AS hours_since_latest_backup
FROM system.backups
WHERE start_time >= now() - INTERVAL {{ inputs.lookback_days }} DAY
- id: classify_backup_health
type: io.kestra.plugin.core.debug.Return
description: Classify operational backup posture based on database reachability,
failure counts, and SLA recency thresholds.
format: |
{{
(outputs.probe_backup_telemetry.row is not defined or outputs.probe_backup_telemetry.row is null) ? 'UNREACHABLE' :
(outputs.probe_backup_telemetry.row.total_backups_evaluated == 0 or outputs.probe_backup_telemetry.row.successful_backups_count == 0) ? 'MISSING_BACKUP' :
(outputs.probe_backup_telemetry.row.failed_backups_count > 0 or outputs.probe_backup_telemetry.row.latest_backup_status != 'BACKUP_CREATED') ? 'CRITICAL' :
(outputs.probe_backup_telemetry.row.hours_since_latest_backup > inputs.max_stale_backup_hours) ? 'STALE' :
'HEALTHY'
}}
- id: route_by_backup_health
type: io.kestra.plugin.core.flow.Switch
description: Route incident notifications and operational confirmations based on
evaluated backup health classification.
value: "{{ outputs.classify_backup_health.value }}"
cases:
HEALTHY:
- id: log_healthy_status
type: io.kestra.plugin.core.log.Log
description: Log verified operational backup health with latest backup metadata
and size metrics.
message: "HEALTHY: ClickHouse backup pipeline nominal. Latest backup '{{
outputs.probe_backup_telemetry.row.latest_backup_name }}' ({{
outputs.probe_backup_telemetry.row.latest_backup_files }} files, {{
outputs.probe_backup_telemetry.row.latest_backup_size_bytes }}
bytes) completed {{
outputs.probe_backup_telemetry.row.hours_since_latest_backup }}h
ago. Zero failures across {{
outputs.probe_backup_telemetry.row.total_backups_evaluated }}
audited jobs."
STALE:
- id: alert_stale_backup
type: io.kestra.plugin.slack.notifications.SlackIncomingWebhook
description: Dispatch Slack warning when no successful backup has executed
within the configured SLA staleness threshold.
url: "{{ secret('SLACK_WEBHOOK_URL') }}"
payload: |
{
"text": ":warning: *ClickHouse Backup SLA Breach Detected*\n*Host:* `{{ inputs.clickhouse_host }}:{{ inputs.clickhouse_port }}`\n*Last Successful Backup:* `{{ outputs.probe_backup_telemetry.row.latest_backup_name }}`\n*Hours Elapsed:* `{{ outputs.probe_backup_telemetry.row.hours_since_latest_backup }}` hours (SLA Limit: `{{ inputs.max_stale_backup_hours }}` hours)\n*Action Required:* Verify automated backup scheduling daemons and confirm target storage availability."
}
CRITICAL:
- id: alert_backup_failure
type: io.kestra.plugin.slack.notifications.SlackIncomingWebhook
description: Dispatch urgent Slack incident alert when ClickHouse system.backups
detects failed backup jobs or error strings.
url: "{{ secret('SLACK_WEBHOOK_URL') }}"
payload: |
{
"text": ":rotating_light: *CRITICAL: ClickHouse Backup Execution Failure*\n*Host:* `{{ inputs.clickhouse_host }}:{{ inputs.clickhouse_port }}`\n*Failed Backups Count:* `{{ outputs.probe_backup_telemetry.row.failed_backups_count }}`\n*Latest Backup Name:* `{{ outputs.probe_backup_telemetry.row.latest_backup_name }}`\n*Latest Status:* `{{ outputs.probe_backup_telemetry.row.latest_backup_status }}`\n*Error Detail:* ```{{ outputs.probe_backup_telemetry.row.latest_backup_error }}```\n*Action Required:* Inspect ClickHouse server logs, check disk/S3 permissions, and ensure backup storage has sufficient capacity."
}
MISSING_BACKUP:
- id: alert_missing_backup
type: io.kestra.plugin.slack.notifications.SlackIncomingWebhook
description: Dispatch critical alert when zero successful backups are found in
the entire lookback window.
url: "{{ secret('SLACK_WEBHOOK_URL') }}"
payload: |
{
"text": ":sos: *CRITICAL: No Successful ClickHouse Backups Found*\n*Host:* `{{ inputs.clickhouse_host }}:{{ inputs.clickhouse_port }}`\n*Audited Window:* Past `{{ inputs.lookback_days }}` days\n*Status:* Zero successful backups recorded in `system.backups` catalog. Disaster recovery recovery point objective (RPO) is compromised."
}
UNREACHABLE:
- id: alert_unreachable_endpoint
type: io.kestra.plugin.slack.notifications.SlackIncomingWebhook
description: Dispatch urgent Slack alert when ClickHouse server cannot be
contacted via JDBC.
url: "{{ secret('SLACK_WEBHOOK_URL') }}"
payload: |
{
"text": ":satellite_antenna: *ClickHouse Server Unreachable*\n*Host:* `{{ inputs.clickhouse_host }}:{{ inputs.clickhouse_port }}`\n*Status:* Unable to establish JDBC connection to query `system.backups`. Ensure ClickHouse server is running and network security groups permit traffic."
}
- id: log_audit_summary
type: io.kestra.plugin.core.log.Log
description: Record immutable audit trail of ClickHouse backup verification for
disaster recovery compliance.
message: |
[AUDIT] ClickHouse Automated Backup Sentinel completed.
Host: {{ inputs.clickhouse_host }}:{{ inputs.clickhouse_port }} | Database: {{ inputs.database_name }}
Classification: {{ outputs.classify_backup_health.value }}
Total Evaluated: {{ outputs.probe_backup_telemetry.row.total_backups_evaluated ?? 0 }}
Failed Backups: {{ outputs.probe_backup_telemetry.row.failed_backups_count ?? 0 }}
Successful Backups: {{ outputs.probe_backup_telemetry.row.successful_backups_count ?? 0 }}
Latest Backup: {{ outputs.probe_backup_telemetry.row.latest_backup_name ?? 'NONE' }} (Status: {{ outputs.probe_backup_telemetry.row.latest_backup_status ?? 'NONE' }})
Hours Since Latest: {{ outputs.probe_backup_telemetry.row.hours_since_latest_backup ?? 'N/A' }}h (Max Allowed: {{ inputs.max_stale_backup_hours }}h)
Execution ID: {{ execution.id }}
errors:
- id: alert_flow_failure
type: io.kestra.plugin.slack.notifications.SlackIncomingWebhook
description: Alert Slack if the ClickHouse sentinel flow encounters an unhandled
runtime error.
url: "{{ secret('SLACK_WEBHOOK_URL') }}"
payload: |
{
"text": ":rotating_light: *ClickHouse Sentinel Flow Error*\nFlow `{{ flow.id }}` failed unexpectedly during execution `{{ execution.id }}`. Backup monitoring is interrupted; inspect task execution logs."
}
outputs:
- id: status
type: STRING
value: "{{ outputs.classify_backup_health.value }}"
- id: failed_backups
type: INT
value: "{{ outputs.probe_backup_telemetry.row.failed_backups_count ?? 0 }}"
- id: latest_backup_status
type: STRING
value: "{{ outputs.probe_backup_telemetry.row.latest_backup_status ?? 'UNKNOWN'
}}"