Chapter 14 · Asynchronous Replication, GTIDs, Parallel Apply, and Topology Design

Lag, Breakage, Data Drift, Rebuilds, Promotion, and Replication Incident Runbooks

Diagnose MariaDB replication incidents by transport, apply, correctness, retention, and authority; repair a controlled break; choose rebuild boundaries; and execute promotion with explicit catch-up and fencing gates.

Advanced150–190 minutesReplication incident + promotion labMariaDB Community 12.3.2 baselineCurriculum anchor: 11.8 LTS · verify target versionLast reviewed: August 2026

Learning outcomes

Replication incidents are rarely “replication is down.” One system may be unable to fetch new binlogs; another may have all events locally but be blocked applying DDL; another may be green but silently drifted after an operator skipped an error. Recovery starts by locating the failure stage and preserving evidence before changing state.

01

Triage transport lag, apply lag, stopped threads, retention gaps, and data drift using status plus application evidence.

02

Create and repair a controlled duplicate-key break without using skip-counter as the first response.

03

Decide when a replica should be repaired in place versus rebuilt from a trusted Chapter 13 backup.

04

Write a promotion procedure that includes write freeze, catch-up proof, fencing, routing, and rollback boundaries.

05

Explain why core asynchronous replication is not an automatic failover control plane.

Incident rule

Before RESET REPLICA, GTID edits, event skipping, binlog purge, promotion, or rebuild, capture the current status, GTID variables, logs, topology, application write target, and a recovery copy/snapshot where appropriate. These commands can destroy the very evidence needed to determine data loss.

1. Triage by stage, not by one lag number

Symptom Likely stage First evidence Typical causes
Slave_IO_Running=No transport Last_IO_Error, error log, DNS/TCP/TLS credentials, firewall, primary unavailable, purged required binlog
I/O Yes, SQL No apply Last_SQL_Error, relay/applied GTID gap duplicate key, schema mismatch, DDL/lock problem
both Yes, large apply gap throughput/blocking GTID I/O vs slave pos, lag timestamps, processlist worker saturation, hot rows, DDL, slow storage
both Yes, wrong query result correctness/drift filters, checksums/invariants, local writes intentional filters, skipped events, manual writes
I/O repeatedly fails after long outage retention requested GTID/binlog vs SHOW BINARY LOGS primary purged history before replica caught up

On 11.6+, Master_last_event_time, Slave_last_event_time, and Master_Slave_time_diff offer additional timing evidence. They complement GTID and thread state; they do not replace application-level correctness checks.

2. Independent incident lab: manufacture a duplicate-key stop safely

yaml · compose.yaml
services:  primary:    image: mariadb:12.3.2    container_name: mdb14-primary    environment:      MARIADB_ROOT_PASSWORD: labroot    command:      - --server-id=141      - --log-bin=mariadb-bin      - --binlog-format=ROW      - --gtid-domain-id=14      - --gtid-strict-mode=ON    ports:      - "33141:3306"  replica:    image: mariadb:12.3.2    container_name: mdb14-replica    environment:      MARIADB_ROOT_PASSWORD: labroot    command:      - --server-id=142      - --log-bin=replica-bin      - --log-slave-updates=ON      - --relay-log=relay-bin      - --gtid-domain-id=14      - --gtid-strict-mode=ON      - --read-only=ON    ports:      - "33142:3306"
shell · start fresh
docker compose down -vdocker compose up -d
sql · primary
CREATE USER IF NOT EXISTS 'repl'@'%' IDENTIFIED BY 'lab-repl';GRANT REPLICATION REPLICA ON *.* TO 'repl'@'%';-- Disposable lab only: discard initialization-era binlogs before application data.RESET MASTER;SHOW MASTER STATUS;SELECT @@server_id, @@gtid_domain_id, @@global.gtid_binlog_pos;
sql · replica
STOP REPLICA;RESET REPLICA ALL;SET GLOBAL gtid_slave_pos='';CHANGE MASTER TO  MASTER_HOST='primary',  MASTER_PORT=3306,  MASTER_USER='repl',  MASTER_PASSWORD='lab-repl',  MASTER_USE_GTID=slave_pos;START REPLICA;SHOW REPLICA STATUS\G
sql · primary — create fixture
CREATE DATABASE IF NOT EXISTS servicehub_replication_lab;CREATE TABLE IF NOT EXISTS servicehub_replication_lab.tickets (  ticket_id BIGINT PRIMARY KEY,  customer_id BIGINT NOT NULL,  state VARCHAR(20) NOT NULL,  updated_at TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6)             ON UPDATE CURRENT_TIMESTAMP(6)) ENGINE=InnoDB;INSERT INTO servicehub_replication_lab.tickets(ticket_id,customer_id,state)VALUES (1,101,'open'),(2,102,'open')ON DUPLICATE KEY UPDATE state=VALUES(state);

Wait until the replica has both rows. Now inject drift on the replica without writing a new GTID: a privileged lab session disables binary logging locally, temporarily turns off read_only, and inserts a row that the primary will later create legitimately.

sql · replica — deliberately create unlogged local drift
SET SESSION sql_log_bin=0;SET GLOBAL read_only=OFF;INSERT INTO servicehub_replication_lab.tickets(ticket_id,customer_id,state)VALUES (777,777,'rogue-local');SET GLOBAL read_only=ON;SELECT * FROM servicehub_replication_lab.tickets WHERE ticket_id=777;
sql · primary — create the authoritative row with the same key
INSERT INTO servicehub_replication_lab.tickets(ticket_id,customer_id,state)VALUES (777,777,'authoritative');
sql · replica — diagnose the stopped SQL thread
SHOW REPLICA STATUS\GSELECT @@global.gtid_slave_pos;SELECT * FROM servicehub_replication_lab.tickets WHERE ticket_id=777;
Expected symptom

The replica SQL thread should stop on a duplicate-key apply error while the I/O thread may remain healthy and continue receiving later events. Exact numeric error text can vary, so key on Slave_SQL_Running, Last_SQL_Errno/Last_SQL_Error, and the conflicting row.

3. Wrong repair: skip until the dashboard is green

sql_slave_skip_counter exists, but skipping an event changes history. If the failed event was the authoritative INSERT and you skip it, the replica retains the rogue value and continues with permanent drift. A green thread state would then be cosmetically better and logically worse.

Do not make “skip one” the default runbook

First establish whether the event can be safely made idempotent or whether the local conflicting data is wrong. If you cannot prove that, rebuild from a trusted snapshot rather than guessing.

In this deliberately controlled lab, we know the local row is rogue and the primary row is authoritative. Remove only the rogue local row with binary logging still disabled, then restart SQL apply.

sql · replica — evidence-based repair for this known lab condition
SET SESSION sql_log_bin=0;SET GLOBAL read_only=OFF;DELETE FROM servicehub_replication_lab.tickets WHERE ticket_id=777;SET GLOBAL read_only=ON;START REPLICA SQL_THREAD;SHOW REPLICA STATUS\GSELECT * FROM servicehub_replication_lab.tickets WHERE ticket_id=777;

The repaired row should now contain authoritative. This repair is valid only because the lab's source of truth is known. In an ambiguous production incident, compare data and transaction history before deleting anything.

4. Retention loss: when repair becomes rebuild

If a replica is offline longer than the primary retains required binlogs, the I/O thread cannot fetch the missing history. GTIDs make the missing position obvious but cannot recreate purged events. The correct recovery path is usually to reseed the replica from a validated logical/physical backup that is consistent with an available GTID/binlog position, then resume from there.

Situation Repair in place? Rebuild bias
temporary network failure, history retained usually yes low
known single rogue local row, authoritative event retained possibly, with proof medium
required binlogs purged no complete replay path high
unknown volume of skipped events/local writes hard to prove clean high
schema drift across many objects risky high

Chapter 13's restore drills become the replica rebuild primitive here. Recovery architecture is intentionally cumulative.

5. Promotion is a sequence of safety gates

A planned switchover is the easiest place to learn promotion discipline. The essential principle is one writable authority at a time. Core MariaDB replication does not enforce that across hosts for you.

  1. Freeze new writes at the application/proxy/network layer or otherwise establish a maintenance write boundary.
  2. Capture the old primary's final GTID position after the last accepted commit.
  3. Wait for the candidate to receive and apply through that exact position; verify row/application invariants.
  4. Fence the old primary so stale clients cannot continue writing to it.
  5. Stop replica threads on the candidate, record its GTID/binlog state, and enable its write role.
  6. Move client routing with health checks and rollback criteria.
  7. Repoint/rebuild old replicas to the new authoritative primary using GTID state.
sql · candidate — evidence before promotion
SHOW REPLICA STATUS\GSELECT @@global.gtid_slave_pos, @@global.gtid_binlog_pos, @@global.gtid_current_pos;-- Compare with the final GTID captured from the old primary.
sql · candidate — only after external write freeze and catch-up proof
STOP REPLICA;-- Keep the replication metadata until rollback/follow-up decisions are complete.SET GLOBAL read_only=OFF;
Do not RESET REPLICA immediately

RESET REPLICA ALL deletes connection metadata and relay logs. MariaDB also documents that it does not reset gtid_slave_pos. Preserve evidence and rollback options until the promotion is accepted.

6. Demoting/repointing the old primary

After the new primary is accepted and the old server is safe to reuse, MariaDB 10.10+ provides MASTER_DEMOTE_TO_SLAVE=1 as a safer role-transition mechanism than blindly using MASTER_USE_GTID=current_pos. It merges the former primary's binlog position into the replica position at change time and forces slave-position semantics.

sql · old primary — conceptual 10.10+ demotion after fencing
STOP REPLICA;CHANGE MASTER TO  MASTER_HOST='new-primary',  MASTER_USER='repl',  MASTER_PASSWORD='rotated-secret',  MASTER_DEMOTE_TO_SLAVE=1;START REPLICA;SHOW REPLICA STATUS\G

Do not run this blindly if the old primary contains accepted writes that never reached the promoted server. First decide whether those transactions are lost, must be merged, or invalidate the promotion.

7. Incident evidence packet

sql · replica — minimum database evidence
SELECT VERSION();SHOW REPLICA STATUS\GSELECT @@global.gtid_binlog_pos, @@global.gtid_slave_pos, @@global.gtid_current_pos;SHOW BINARY LOGS;SHOW VARIABLES WHERE Variable_name IN ('server_id','gtid_domain_id','gtid_strict_mode','read_only','slave_parallel_threads','slave_parallel_mode');SHOW PROCESSLIST;
  • Server/OS/container resource state: CPU, memory pressure, disk latency/free space, network reachability.
  • Replication error-log lines around the first failure, not only the latest retry.
  • Topology/routing evidence: which endpoint applications were actually writing to.
  • Recent DDL/deployments/filter changes and any use of skip/reset commands.
  • Last validated backup plus binlog/GTID coverage sufficient for rebuild.

8. Async replication incident runbook

Gate Question Action if no
Authority Do we know which node is allowed to accept writes? fence/routing control before promotion
Transport Can replica fetch the required history? fix network/auth or rebuild if purged
Apply Can SQL thread apply without unresolved conflicts? diagnose exact event/lock/schema
Completeness Are filters/skips/local writes understood? reconcile or rebuild
Catch-up Has candidate applied the final accepted GTID? do not promote as zero-loss
Recovery Is there a validated backup if repair fails? protect evidence; restore capability first

9. Production judgment and bridge to Galera

Async replication is excellent for read scaling, offloaded backups, migrations, delayed recovery copies, and geographically decoupled replicas when the application understands staleness. It can also support failover, but the safety properties come from the surrounding operating model: fencing, health assessment, candidate selection, client routing, backup/rebuild, and practiced runbooks.

Chapter 15 introduces Galera Cluster, where writes are replicated as write sets and nodes participate in certification/quorum. That changes the failure model, but it still does not remove the need for client routing, fencing, backup, or workload-aware conflict analysis.

Check your understanding

  1. What is the first distinction to make when replication is “lagging”?
  2. Why can sql_slave_skip_counter make a green replica less correct?
  3. When do purged required binlogs force a rebuild?
  4. What must happen before a replica is safely promoted?
  5. What does MASTER_DEMOTE_TO_SLAVE solve, and what does it not solve?
Review the answers

First separate transport from apply and then check correctness/drift. Skipping an event changes history and can preserve wrong data while making threads run again. If the replica cannot obtain required missing history from the primary or another trusted source, it needs reseeding from a validated backup/snapshot. Safe promotion requires a write freeze, exact catch-up proof, fencing of the old writer, correctness checks, and controlled client routing. MASTER_DEMOTE_TO_SLAVE improves GTID role-transition bookkeeping on supported versions; it does not reconcile lost/divergent writes or provide automatic failover.

shell · cleanup the incident lab
docker compose down -v

Authoritative references

Keep knowledge open

Help the academy stay free and grow.

If these tutorials save you time, a small donation supports new lessons, technical review, diagrams, examples, and long-term maintenance.

ETHEthereum / ERC-20 only
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0

Send only assets compatible with the Ethereum/ERC-20 network. Do not send TRC-20/TRON assets.