Schedule icon
Query icon
OutputValues icon
Log icon
Switch icon
SlackIncomingWebhook icon
Fail icon
If icon
Pause icon
Sleep icon

MySQL Stopped Replication Recovery Guard

Detect a stopped MySQL replica thread, restart only a transient stop behind a dry-run and approval gate, then re-read status and fail if not resumed.

Categories
DataInfrastructure

A MySQL read replica can lose its IO or SQL thread after a transient fault such as a network blip or a dropped connection to the source. The threads stay down, the replica falls behind in silence and keeps serving stale reads or breaks a failover. This guard reads the replica state, restarts replication only when the stop is transient and safe, then proves the threads came back.

This blueprint was created by zkasuran.

Safety

  • Read-only by default. dry_run is true and auto_remediate is false, so a plain run reports and never restarts.
  • A restart happens only when auto_remediate is true and dry_run is false. It then waits for a human to resume the Pause with approved set true. An unanswered gate reads as not approved.
  • Only a transient stop is restarted. A stop that carries a non-zero Last_SQL_Errno is a data error, so the guard refuses the restart and fails loudly. START REPLICA on a data error would skip the failing event and drift the replica from the source.
  • The guard never sets sql_slave_skip_counter and never skips an event. It only runs START REPLICA and only for the transient case.

How it works

  1. detect (io.kestra.plugin.jdbc.mysql.Query) reads SHOW REPLICA STATUS from the replica. Zero rows means the target is not a replica.
  2. classify (io.kestra.plugin.core.output.OutputValues) reads the status row and sorts it into one state. HEALTHY is both threads Yes with lag within the ceiling. LAGGING is both threads Yes with lag over the ceiling. STOPPED_TRANSIENT is a thread not Yes with Last_SQL_Errno zero. STOPPED_DATA_ERROR is a thread not Yes with a non-zero Last_SQL_Errno. NOT_A_REPLICA is zero rows.
  3. log_status (io.kestra.plugin.core.log.Log) records the classification with the raw status fields.
  4. route (io.kestra.plugin.core.flow.Switch) branches on the classification. HEALTHY logs and ends. LAGGING alerts and ends because both threads already run. STOPPED_DATA_ERROR and NOT_A_REPLICA alert and fail.
  5. For STOPPED_TRANSIENT, transient_alert posts the outage. mode (io.kestra.plugin.core.flow.If) enters remediation only when auto_remediate is true and dry_run is false. Otherwise report_only logs what would run.
  6. approval_gate (io.kestra.plugin.core.flow.Pause) holds the run until a human resumes it with approved. restart_replica runs START REPLICA only when approved is true.
  7. reverify re-reads SHOW REPLICA STATUS. verify_result fails the run through io.kestra.plugin.core.execution.Fail when the two threads are not both running again.

What you get

  • A recovery step that acts only on a safe transient stop and leaves a data-error stop for a human.
  • A gate that is safe to leave scheduled. It reports until an operator turns on remediation and approves each run.
  • A real verification step. The run passes only when the second read of SHOW REPLICA STATUS shows both threads running.
  • Slack alerts on every stop, on a lagging replica and on any failure.

Who it's for

  • Platform engineers who run MySQL read replicas and want a safe self-heal for the common transient stop.
  • DBAs who need the dangerous data-error stop surfaced loudly rather than papered over by an auto-skip.
  • Teams evaluating the MySQL plugin who want an example that reads replica telemetry and runs gated recovery.

Why orchestrate this with Kestra

MySQL has no scheduler, no approval step and no way to prove a restart worked. Kestra adds the missing pieces. It polls the replica, gates the restart behind dry-run and approval, re-reads SHOW REPLICA STATUS to turn a hopeful restart into a verified one, applies one connection to every task and posts a Slack alert the moment a thread stops or a step fails.

Tested end to end

Run on Kestra 2.0.5 against a MySQL 8.0 source and replica pair with GTID replication.

  • Detector can fail: a healthy pair returned HEALTHY with both threads Yes, the same replica after STOP REPLICA returned STOPPED_TRANSIENT.
  • Dry run on a transient stop detected it, alerted and left both threads down (confirmed with the mysql client, No then No).
  • Remediate with approval ran START REPLICA and the re-read reported both threads Yes (No then Yes in SHOW REPLICA STATUS).
  • The approval gate held. A run resumed with approved false ran no restart.
  • A data-error stop (duplicate key, Last_SQL_Errno 1062) was refused. No START REPLICA ran and Exec_Source_Log_Pos stayed put.
  • A re-read after a restart against an unreachable source failed the run rather than reporting success.
  • An unreachable replica failed the detect step and fired the Slack alert.

Prerequisites

A MySQL read replica reachable over JDBC and a user that can run SHOW REPLICA STATUS and START REPLICA. The connecting user needs the REPLICATION CLIENT privilege and the REPLICATION_SLAVE_ADMIN privilege.

Secrets

  • MYSQL_USERNAME: MySQL user, read when use_secret_auth is true.
  • MYSQL_PASSWORD: password for that user.
  • SLACK_WEBHOOK_URL: used by the alerts and the errors block.
  • In Kestra OSS, provide each secret as a base64-encoded environment variable prefixed SECRET_.

Inputs

Name Type Default Description
replica_url STRING jdbc:mysql://localhost:3306/ JDBC URL of the replica to guard.
use_secret_auth BOOL true Read credentials from secrets. Set false only for inline test credentials.
mysql_username STRING root Username used only when use_secret_auth is false.
mysql_password STRING `` Password used only when use_secret_auth is false.
max_lag_seconds INT 30 Lag ceiling in seconds for the lagging state.
dry_run BOOL true Report without restarting.
auto_remediate BOOL false Restart a transient stop behind the approval gate when dry_run is false.

Quick start

  1. Set MYSQL_USERNAME, MYSQL_PASSWORD and SLACK_WEBHOOK_URL as namespace secrets.
  2. Import this flow.
  3. Run it with replica_url pointed at a read replica. The default dry run reports the replica state.
  4. To recover a transient stop, run again with dry_run false and auto_remediate true, then resume the paused execution with approved true.

How to extend

  • Loop over several replicas from one flow by moving the detect and restart steps into a replica loop.
  • Add a channel name to the SQL for a multi-source replica, for example SHOW REPLICA STATUS FOR CHANNEL 'ch1'.
  • Open a ticket on a STOPPED_DATA_ERROR classification so the human handoff is tracked.
  • Fail the run as soon as detection finds any stop, for a read-only drift check with no remediation.

Links

See How

New to Kestra?

Use blueprints to kickstart your first workflows.