If icon
Fail icon
Query icon
Log icon
SlackIncomingWebhook icon
Pause icon
Parallel icon
Capture icon
Sequential icon
Sleep icon
IonToJson icon
Schedule icon

MySQL Binlog Row Image Guard for Debezium CDC

Detect a reduced MySQL binlog_row_image that stops Debezium, set FULL after approval and prove it with a captured canary update.

Categories
DataInfrastructure

Safety

With binlog_row_image set to MINIMAL or NOBLOB MySQL writes only part of each row into the binary log. Debezium refuses to start on such a server, so change capture stops and every consumer behind it goes quiet. This flow reads the setting, changes only that one variable after an approval and proves the result with a real capture.

Guard What it stops
Detector on log_bin, binlog_format and binlog_row_image Acting on a server where the row image does not apply
Disabled schedule and safe input pins An unattended change from the scheduled path
Pause and a fresh read after it Applying an approval to a server an operator already fixed
One variable, no restart and no retry Touching anything but binlog_row_image
Debezium capture of a canary update Reporting success while the change stream still fails
Count of sessions with a reduced image Calling the repair done while open sessions keep writing partial rows
Flow level queue with limit 1 Two runs changing the setting together

How it works

  1. validate_names accepts only simple identifiers for the database and the canary table.
  2. read_settings and read_sessions (io.kestra.plugin.jdbc.mysql.Query) read the global settings and count client sessions whose own value is not FULL. Sessions opened by this flow carry a connection attribute and are left out.
  3. A server with row based logging and a reduced image gets a Slack alert from alert. The default path ends there.
  4. With auto_remediate true and dry_run false, approval (io.kestra.plugin.core.flow.Pause) waits. recheck_settings then reads the value again and a server that is already fixed is left alone.
  5. read_canary reads the canary row, set_row_image runs SET GLOBAL (or SET PERSIST when persist_setting is true) and verify_setting reads the value back.
  6. prove_capture (io.kestra.plugin.core.flow.Parallel) starts io.kestra.plugin.debezium.mysql.Capture on the canary table and updates the canary row from a second branch on a fresh connection. before_image_incomplete compares the captured before image with the row read in step 5 and fails the run if they differ.
  7. read_sessions_after counts sessions that still write reduced images. A non-zero count ends the run as WARNING because the stream is repaired for new sessions only.

What you get

  • A report of the row image setting and of the sessions that still write reduced images.
  • A single approved change to one variable, with the old and new value in the execution log.
  • Proof from Debezium itself that a canary update arrives with a full before image.
  • A clear WARNING when long-lived connections have to be recycled.

Who it's for

  • Data platform teams that stream MySQL changes with Debezium into a warehouse or a Kafka topic.
  • DBAs who manage MySQL parameters across restarts, upgrades and managed service parameter groups.
  • Anyone who found a CDC pipeline silent after a MySQL change.

Why orchestrate this with Kestra

MySQL can set the variable and Debezium can read the log, but nothing connects the two. Kestra reads the setting, holds the approval, changes it once, starts a real capture while it updates the canary row and keeps the whole proof in one execution with a Slack alert and a failure state.

Tested end to end

Kestra 2.0.6 with the bundled MySQL, Debezium and Slack tasks against MySQL 8.0.36 with log_bin=ON, binlog_format=ROW and binlog_row_image=MINIMAL. With that setting a stand-alone Debezium capture refused to start (execution 6zmLJCSCcJU03HKuUn6hjT: "The database server is not configured to use a FULL binlog_row_image"). After the approved repair the same capture read an update of the canary row with a complete before image and every outcome was confirmed with the mysql client inside the server container.

A session that was already open when the setting changed kept writing reduced row images: the flow found it, finished with WARNING and said which sessions to recycle. The flow's own connections are tagged so they are never counted. The account password is a SECRET input and was absent from every execution and log.

Case Execution Result
Healthy: row image is FULL 15upvmAh2vGReyyg7ypeRf SUCCESS. No alert, no write
Dry run: row image is MINIMAL 39UbK2s0Igbv7QVlHfwK2u SUCCESS. Alert sent, report only, setting still MINIMAL
Declined approval 5bMR6r5Rdx06YPVy59ctg3 SUCCESS. Setting still MINIMAL, canary untouched
Approved repair 7Xt4VPezzZmmDu3xB3R033 SUCCESS. Global value FULL, Debezium captured the canary update and its before image matched the row read just before; canary updated exactly once
Repeat 1r1gHatDBPaZ8IMV14DmpD SUCCESS. Healthy branch, no write, no approval
Account without SYSTEM_VARIABLES_ADMIN 9JKrVxOIgCD3wlkidC1L8 FAILED as intended. MySQL refused the change, the setting stayed MINIMAL and the canary was not touched
Operator fixed the setting during the Pause 43LipqMYicS2CPK4tHmtUo SUCCESS. Recheck found FULL, no write, canary untouched
Existing session keeps the reduced image 1KXFTywm7kkohu0PswWsA WARNING as intended. Global value FULL and capture proved, one open session still writes reduced images until it reconnects
FULL globally, one session still reduced 6CvWqe0ys0DOGIZ7U5LQIV WARNING as intended. Reported without any write
Statement or mixed logging 1jSLjv2GgkzTxPx4wQP4Jj SUCCESS. Row image does not apply, no alert, no write
Repair with SET PERSIST 2n3wlK5rkVsUAZi7Gj42CJ SUCCESS. Global and persisted value FULL
Invalid canary table name 57oxmUvDhZq3BMK047gJAL FAILED as intended before any query
Second MySQL: healthy 4isveeTERE68pPce42Mcyq SUCCESS
Second MySQL: dry run 4bGu9qSUAzua1JHCwVfbN2 SUCCESS
Second MySQL: approved repair 6fIBkWwPeuPng96EVeqGD6 SUCCESS. Same proof
Second MySQL: fixed during the Pause 1x1R1RPWKS3kYNS1Ircvoe SUCCESS

Prerequisites

  • Kestra OSS 2.0.6 or later with the MySQL, Debezium and Slack plugins.
  • MySQL 8.0 with log_bin=ON, binlog_format=ROW, a unique server_id and GTIDs or file positions that Debezium can read.
  • A canary table with an id primary key, a marker column and a counter column and one row in it. Only that row is ever updated.
  • A MySQL account (use mysql_native_password if your Debezium build cannot use caching_sha2_password) with SELECT on the canary database and on performance_schema, UPDATE on the canary table, REPLICATION SLAVE, REPLICATION CLIENT, RELOAD, SHOW DATABASES and LOCK TABLES for the Debezium schema snapshot and SYSTEM_VARIABLES_ADMIN for a repair.

Secrets

  • MYSQL_USERNAME and MYSQL_PASSWORD: the account above.
  • SLACK_WEBHOOK_URL: incoming webhook for the alert and the failure handler.

Inputs

Input Purpose Default
mysql_host, mysql_port Server used by JDBC and by Debezium localhost, 3306
mysql_database Database that holds the canary table cdc_demo
canary_table, canary_id Canary table and row cdc_canary, 1
mysql_username, mysql_password Account; the password is a SECRET input from secrets
debezium_server_id Replica id used by the capture; must be unique 40401
persist_setting Use SET PERSIST so a restart keeps FULL false
auto_remediate Allow the approval and repair branch false
dry_run Must be false before any change true

Quick start

  1. Create the canary table and row and grant the account its privileges.
  2. Add the three secrets and run the flow with the defaults. It only reads.
  3. If the setting is reduced, review the Slack alert. A change enlarges every row event in the binary log, so check log volume and replica capacity first.
  4. Run again with auto_remediate=true and dry_run=false, review the Pause and resume with approved=true.
  5. If the run ends in WARNING, recycle the connection pools listed by your application owners and run the report again.

How to extend

  • Set persist_setting to true on self-managed servers. On managed services change the parameter group instead, because a restart restores the group value.
  • Add a second capture of another table by adding a table to table.include.list.
  • Send the WARNING to the owning team with SlackIncomingWebhook and the session list from performance_schema.threads.

Limits: the flow changes the live value only. The next restart restores whatever the configuration file or parameter group says. Sessions that were open before the change keep their old value and the flow cannot recycle them. Capture uses properties for table.include.list because the dedicated includedTables property was not rendered by this plugin version.

Links

This blueprint was created by zkasuran.

See How

New to Kestra?

Use blueprints to kickstart your first workflows.