Chapter 14 · Asynchronous Replication, GTIDs, Topologies, and Operational Safety
Lag Diagnosis, Broken Replication, Data Drift, Re-Synchronization, and Promotion Runbooks
Diagnose lag and stopped replication from receiver/applier/worker evidence, distinguish operational failure from silent drift, and build a promotion/resynchronization runbook with fencing and post-promotion correctness checks.
Learning outcomes
A replica alarm that says “lag = 120 seconds” is the start of an investigation, not a diagnosis. The receiver may be disconnected, the applier may be stopped on an error, parallel workers may be retrying transactions, the server may be resource-saturated, or the data may have drifted even while both threads report healthy. This final lesson builds a repeatable evidence tree and a promotion runbook.
Diagnose receiver failure, applier failure, worker retries, and resource-bound lag using correlated evidence.
Distinguish lag from stopped replication and both from silent data drift.
Use business invariants and GTID-set comparisons before deciding whether to repair, skip, rebuild, or promote.
Define when reprovisioning is safer than manual transaction skipping.
Write a promotion runbook with fencing, catch-up, candidate validation, role change, and post-promotion checks.
A practical diagnosis tree
| Question | Evidence | Likely branch |
|---|---|---|
| Can receiver connect? | replication_connection_status + error log | network/TLS/auth/source availability |
| Is applier running? | replication_applier_status / SHOW REPLICA STATUS | SQL error, operator stop, delay |
| Are workers making progress? | replication_applier_status_by_worker timestamps/errors/retries | lock contention, transaction retries, large transaction |
| Is server saturated? | CPU, I/O, memory, Performance Schema waits | resource bottleneck or reporting interference |
| Are GTID sets plausible? | gtid_executed/purged + GTID_SUBTRACT | missing/errant history |
| Is data correct? | checksums/business invariants | drift or incomplete provisioning |
Always timestamp evidence. Replication state changes while you investigate, so a screenshot of one field without time/correlation can mislead.
Controlled failure 1: stop the applier, not the receiver
STOP REPLICA SQL_THREAD;SELECT CHANNEL_NAME, SERVICE_STATE, LAST_QUEUED_TRANSACTIONFROM performance_schema.replication_connection_status;SELECT CHANNEL_NAME, SERVICE_STATE, REMAINING_DELAYFROM performance_schema.replication_applier_status;INSERT INTO servicehub_repl_lab.work_orders(site_code,status,summary,priority)VALUES ('BAKU-01','OPEN','Lag drill 1',3), ('BAKU-01','OPEN','Lag drill 2',3), ('BAKU-02','OPEN','Lag drill 3',3);SELECT CHANNEL_NAME, SERVICE_STATE, SOURCE_UUID, LAST_QUEUED_TRANSACTION, QUEUEING_TRANSACTIONFROM performance_schema.replication_connection_status;SELECT CHANNEL_NAME, SERVICE_STATE, REMAINING_DELAY, COUNT_TRANSACTIONS_RETRIESFROM performance_schema.replication_applier_status;SELECT CHANNEL_NAME, WORKER_ID, SERVICE_STATE, LAST_APPLIED_TRANSACTION, LAST_ERROR_NUMBER, LAST_ERROR_MESSAGEFROM performance_schema.replication_applier_status_by_workerORDER BY CHANNEL_NAME, WORKER_ID;The receiver can continue queueing while the applier is OFF. The repair is obvious because we created the failure: verify there is no underlying SQL error, restart the applier, then prove catch-up. In a real incident, never start/skip blindly before capturing the stopping error and deciding whether the source transaction is valid.
START REPLICA SQL_THREAD;SHOW REPLICA STATUS\GSELECT COUNT(*) AS lag_drill_rowsFROM servicehub_repl_lab.work_ordersWHERE summary LIKE 'Lag drill %';Controlled failure 2: separate broken replication from drift
Replication can be running while old data is already wrong—for example, after a historical manual change, a bad provisioning snapshot, or a filter that was later removed. Thread health does not repair that past divergence. Build small stable invariants into operational checks.
SELECT COUNT(*) AS total_orders, SUM(status='OPEN') AS open_orders, MIN(work_order_id) AS min_id, MAX(work_order_id) AS max_idFROM servicehub_repl_lab.work_orders;SELECT COUNT(*) AS baseline_markerFROM servicehub_repl_lab.replication_markersWHERE marker_name='CH14_SOURCE_BASELINE';CHECK TABLE servicehub_repl_lab.work_orders, servicehub_repl_lab.replication_markers;Matching aggregate counts are useful but not proof of byte-for-byte equality. Real drift verification may use checksums, application-level hashes, or rebuild-and-compare workflows appropriate to data size. Avoid running unbounded checksum scans on production during peak load without understanding their cost.
When is rebuilding safer than skipping?
If an applier stops because a row is missing, “skip the transaction” may restart the thread while cementing divergence. First answer why the row differs and whether later transactions depend on it.
Prefer reprovision/reclone when the replica's history is not trustworthy, errors span many transactions, filters produced substantial incompleteness, or a clean source snapshot is cheaper to reason about than handcrafted repair. Manual repair can be appropriate for a small understood discrepancy with explicit business ownership and verification, but it should be documented as a data correction—not hidden inside replication control commands.
Promotion is a correctness procedure
Suppose the source fails and the replica is reachable. The fastest command is not necessarily the safest action. Promotion should have explicit gates:
| Gate | Required evidence |
|---|---|
| 1. Fence old writer | application/load balancer/network/storage control prevents two writable primaries |
| 2. Identify candidate | replication configuration role; no intentional filter/delay that invalidates promotion |
| 3. Capture state | receiver/applier status, errors, GTID sets, last applied transaction, source reachability |
| 4. Catch up if possible | apply all safely available relay/source transactions; define accepted RPO if not possible |
| 5. Validate data | ServiceHub invariants and critical-table checks pass |
| 6. Change role | stop/reset only what runbook requires; make candidate writable deliberately |
| 7. Redirect clients | connection endpoint changes; stale pools/sessions handled |
| 8. Validate writes | new transaction commits and reads correctly on promoted source |
| 9. Preserve old source | do not wipe; inspect for transactions absent from promoted node |
| 10. Rebuild topology | reprovision former source/other replicas from new authoritative history |
Disposable promotion drill
This exercise intentionally changes topology roles. Use only the disposable containers and write down the decision before commands.
SHOW REPLICA STATUS\GSELECT @@GLOBAL.gtid_executed AS candidate_gtids;SELECT @@GLOBAL.read_only, @@GLOBAL.super_read_only;SELECT COUNT(*) AS orders, SUM(status='OPEN') AS open_ordersFROM servicehub_repl_lab.work_orders;Now simulate loss of the source container. Confirm the old source is fenced (stopped) before making the candidate writable.
docker compose stop mysql-sourcedocker compose psSTOP REPLICA;SET GLOBAL super_read_only = OFF;SET GLOBAL read_only = OFF;INSERT INTO servicehub_repl_lab.replication_markers(marker_name)VALUES (CONCAT('PROMOTED_WRITE_', UUID()));SELECT marker_name,created_atFROM servicehub_repl_lab.replication_markersORDER BY marker_id DESC LIMIT 3;Do not immediately reuse the old source as a replica. First compare the old source history when it becomes available. If it accepted transactions that the promoted node never received, you have a reconciliation decision. The safest rejoin path is often to rebuild the old node from the new authoritative source rather than assuming bidirectional convergence.
Lag monitoring that survives topology growth
MySQL 8.4 exposes original and immediate commit timestamps plus per-thread processing timestamps in Performance Schema replication tables. Use those to reason about where time is being spent. Traditional seconds-behind values can still help in simple topologies, but they are not a universal correctness metric and can become misleading in more complex chains.
SELECT CHANNEL_NAME, SERVICE_STATE, SOURCE_UUID, LAST_QUEUED_TRANSACTION, QUEUEING_TRANSACTIONFROM performance_schema.replication_connection_status;SELECT CHANNEL_NAME, SERVICE_STATE, REMAINING_DELAY, COUNT_TRANSACTIONS_RETRIESFROM performance_schema.replication_applier_status;SELECT CHANNEL_NAME, WORKER_ID, SERVICE_STATE, LAST_APPLIED_TRANSACTION, LAST_ERROR_NUMBER, LAST_ERROR_MESSAGEFROM performance_schema.replication_applier_status_by_workerORDER BY CHANNEL_NAME, WORKER_ID;Final operational runbook template
INCIDENT / CHANGE ID:Old source:Candidate replica:Time evidence captured:FENCING[ ] Old writer cannot accept application traffic[ ] No alternate writer endpoint remains activeREPLICATION STATE[ ] Receiver state captured[ ] Applier/worker state captured[ ] Last errors/retries captured[ ] Source and candidate GTID sets compared[ ] Filter and SOURCE_DELAY configuration checkedDATA ACCEPTANCE[ ] Critical ServiceHub row/business invariants pass[ ] Accepted RPO documented if candidate is not fully caught upPROMOTION[ ] Candidate made writable[ ] Application endpoint switched[ ] Test write + read-after-write succeedsPOST-PROMOTION[ ] Old source preserved for transaction reconciliation[ ] Other replicas rebuilt/repointed under a reviewed plan[ ] Monitoring/backup jobs now target the promoted source[ ] Incident timeline and corrective action recordedProduction judgment and bridge
Asynchronous replication is operationally useful precisely because it separates source commit from replica application—but that separation creates a measurable data-loss window and requires promotion discipline. Chapter 15 moves to Group Replication and InnoDB Cluster, where quorum, membership, certification, MySQL Shell AdminAPI, and Router change the availability model. The habit from this chapter remains: topology state is evidence, and availability never substitutes for correctness.
Knowledge check
- What is the first thing to do before promoting a replica?
- Why can healthy replication threads coexist with data drift?
- When is rebuild usually safer than transaction skipping?
- Why should the old source be preserved after promotion?
- What three evidence categories should a lag investigation correlate?
Reveal answers
- Fence the old writer so two servers cannot accept authoritative writes simultaneously.
- Threads only describe current transport/application; historical manual changes, bad provisioning, or prior filtering can leave old divergence intact.
- When history is not trustworthy, errors are broad/unclear, or a clean reprovision is easier to verify than a sequence of manual exceptions.
- It may contain transactions that never reached the promoted candidate and therefore matters for reconciliation and incident evidence.
- Replication receiver/applier/worker state, server resource/wait evidence, and GTID/business-data correctness evidence.