At a glance
The alert that fires when a replica’s IO or SQL thread stops, or when Seconds_Behind_Source climbs past 10 seconds. Replication is the safety net behind almost every production MySQL topology: it powers read scaling, it is the basis for failover, and it is what your backups and analytics often run against. When a thread stops, the replica silently freezes in time and stops being a usable failover target. When lag climbs, read replicas start serving stale data. This hero card catches both failure modes the moment they appear, before a failover lands you on a hours-old copy.
Calculation
On each real-time sample the engine runsSHOW REPLICA STATUS against every configured replica and evaluates:
replica_healthy is false for any replica, the alert fires and names the failing replica plus the reason (which thread, or how far behind, plus the error text from Last_Error if present). The lag reading is taken straight from Seconds_Behind_Source, which MySQL computes as the difference between the replica’s clock and the timestamp of the event it is currently applying. The engine is aware of the well-known quirk that Seconds_Behind_Source reports NULL whenever either thread is not running; a NULL is treated as “thread down”, not “zero lag”, which is the safe interpretation. Where the topology uses GTIDs, the engine also cross-checks Retrieved_Gtid_Set against Executed_Gtid_Set to confirm the SQL thread is genuinely keeping up with what the IO thread has fetched.
Worked example
A platform team runs one primary and two read replicas on MySQL 8.0. Replica B serves the reporting and analytics read traffic. Snapshot taken on 09 Jun 26 at 03:40, during an overnight batch load.
The hero card fires for Replica B: SQL thread stopped, error 1062 duplicate entry. Replica A is healthy at 1 second behind. Because B’s SQL thread is down,
Seconds_Behind_Source reads NULL, the engine correctly reports this as “thread stopped”, not “caught up”.
The on-call read:
- A stopped SQL thread means B is frozen. It stopped applying events at the exact statement that errored. Every write to the primary since 03:38 is now unreplicated on B. If a failover picked B right now, the team would lose all writes after that point.
- The error explains why. A 1062 on the SQL thread usually means the row already exists on the replica, classic symptom of a previous failover or a manual write on the replica that diverged it from the primary. The replica has drifted out of sync with the source.
- The fix is a real decision, not a restart. Blindly skipping the event (
SET GLOBAL sql_replica_skip_counter = 1or injecting an empty GTID transaction) clears the error but leaves B permanently inconsistent with the primary. The correct fix is usually to re-clone B from a fresh backup or use a consistency tool to reconcile, then resume. Until then, B must be removed from the read pool so it stops serving stale data, and it must not be a failover candidate.
- A stopped thread is worse than lag. Lag drains; a stopped thread does not move at all until a human intervenes, and it quietly disqualifies the replica as a failover target. The alert treats
IO/SQL != Yesas the most serious state for exactly this reason. NULLlag is a red flag, not a green one. WhenSeconds_Behind_SourceisNULL, replication is not running. Tooling that reads it as “0 = healthy” gets this dangerously backwards; this card treatsNULLas thread-down.- Skipping an error is a trap. It is tempting to skip the offending event and watch replication resume, but every skip widens the drift between replica and primary. Fix the divergence properly (re-clone or reconcile), or you are building a failover target that will silently lose data.
Sibling cards
Reconciling against the source
Where to look in MySQL itself:For GTID topologies, compareSHOW REPLICA STATUS\G(orSHOW SLAVE STATUS\Gpre-8.0.22) for the full picture:Replica_IO_Running,Replica_SQL_Running,Seconds_Behind_Source,Last_Error,Last_IO_Error,Last_SQL_Error.SELECT * FROM performance_schema.replication_connection_status\Gandreplication_applier_status_by_worker\Gfor the modern, per-worker view (especially useful with multi-threaded replication).SELECT * FROM performance_schema.replication_applier_status_by_coordinator\Gto see the coordinator and any apply errors. On the primary:SHOW REPLICAS;(orSHOW SLAVE HOSTS;) to confirm which replicas are still connected.
Retrieved_Gtid_Set (fetched) with Executed_Gtid_Set (applied) to see the apply gap directly rather than trusting Seconds_Behind_Source alone.
Why our number may legitimately differ from a raw SHOW REPLICA STATUS:
Managed-service note: On Amazon RDS and Aurora, replica lag is the
ReplicaLag (RDS read replicas) or AuroraReplicaLag (Aurora cluster) CloudWatch metric in milliseconds; thread state surfaces in the RDS events log and SHOW REPLICA STATUS on the instance. On Google Cloud SQL use database/mysql/replication/seconds_behind_master and the replication state in the instance overview. Note that on Aurora, replication is storage-level rather than binlog-based, so the IO/SQL thread concept applies only to binlog replicas, not in-cluster Aurora replicas.
Known limitations / FAQs
Why does the card sometimes show NULL lag as a failure? BecauseSeconds_Behind_Source returns NULL whenever the IO or SQL thread is not running, not when the replica is caught up. A caught-up replica reads 0, not NULL. The card deliberately treats NULL as “thread down” because the alternative, treating it as zero lag, would hide a frozen replica behind a healthy-looking number. This is the most important safety behaviour of the card.
My replica is 10 seconds behind during a nightly batch and the alert fires every night. Is that wrong?
That is a real lag breach, but it may be benign for your topology if the replica only serves analytics that tolerate staleness. Two options: raise the lag threshold for this profile in the Alert Rules tab to match your batch window, or move the batch to a window where lag is acceptable. If the replica is also a failover target, though, do not relax the threshold, lag on a failover candidate is genuine risk.
The SQL thread stopped with a 1062 duplicate-key error. Can I just skip it?
You can (SET GLOBAL sql_replica_skip_counter = 1, or inject an empty transaction for the failing GTID), and replication will resume, but skipping leaves the replica permanently inconsistent with the primary because you have dropped an event the primary applied. The correct fix for a diverged replica is usually to re-clone it from a fresh backup or reconcile with a consistency-checking tool. Skipping is acceptable only when you are certain the event is genuinely redundant.
What is the difference between the IO thread and the SQL thread stopping?
The IO thread connects to the primary and copies binlog events into the replica’s relay log; if it stops, the replica cannot fetch new changes (usually a network or auth issue, or the source binlog was purged). The SQL thread reads the relay log and applies those events; if it stops, the replica has the data but cannot apply it (usually a data error like a duplicate key or missing row). Both reaching Yes is required for healthy replication.
Why is a stopped replica dangerous even if the application never errors?
Because the failure is silent. The application keeps talking to the primary and sees nothing wrong. But the replica has quietly frozen: it is no longer a valid failover target (promoting it loses every write since it stopped) and, if it is in the read pool, it serves data that is older by the minute. Nothing surfaces this to the application, which is exactly why a hero alert is needed.
The source purged its binlog before my replica read it. What now?
The IO thread errors with “could not find first log file name” (error 1236). The replica cannot fetch the missing events, so it cannot continue from where it left off. The fix is to re-establish the replica from a fresh backup or snapshot and re-point it at a valid binlog position or GTID. To prevent recurrence, increase binlog_expire_logs_seconds on the primary so binlogs are retained long enough for replicas to catch up after an outage. Cross-reference Binlog Backlog (MB) on Primary.
Does this alert understand intentionally delayed replicas?
Yes, where configured. A replica with SOURCE_DELAY set (a deliberate safety lag, for example a 1-hour delayed replica for accidental-delete recovery) is expected to report lag near its configured delay. Tell the connector the intended delay for that replica so the card measures lag relative to the delay rather than firing constantly. The thread-state half of the alert still applies normally.