Chapter 14 · Asynchronous Replication, GTIDs, Parallel Apply, and Topology Design
MariaDB GTIDs, Domains, Positions, Strict Mode, and Failover Semantics
Understand MariaDB GTID domain-server-sequence identities, compare binlog/slave/current positions, enforce strict history discipline, and reason safely about repointing and failover.
Learning outcomes
File names such as mariadb-bin.000018:492731 are
physical coordinates. They work, but failover becomes easier to
reason about when transactions have logical identities that
remain stable across servers. MariaDB's
Global Transaction ID (GTID) is a three-number
tuple domain_id-server_id-sequence_number, for
example 14-141-203.
A domain is an ordered transaction stream. The server ID records where the event group was first logged, and the sequence number increases within the stream. MariaDB's GTID model is not MySQL's UUID-based SID model; never copy failover instructions across vendors without translating semantics and testing exact versions.
Decode MariaDB GTID domain-server-sequence triplets and explain why domains are topology design, not decoration.
Distinguish gtid_binlog_pos, gtid_slave_pos, and gtid_current_pos using observable server state.
Use MASTER_USE_GTID=slave_pos deliberately and explain current_pos hazards when local transactions exist.
Use gtid_strict_mode as a guardrail and diagnose divergent local history.
Explain how GTID positions simplify repointing while still requiring fencing and data-loss checks.
1. Three GTID positions answer three different questions
| Variable | Mental model | Operational question |
|---|---|---|
gtid_binlog_pos |
GTIDs represented in this server's own binary log | What history has this server itself logged? |
gtid_slave_pos |
GTIDs applied by replication from upstream | How far has replication replay advanced? |
gtid_current_pos |
combined current position derived from local-binlog and replicated state | What GTID state does this server currently represent overall? |
The distinction matters on a replica that also executes local
binlogged transactions. gtid_current_pos can then
advance for reasons unrelated to upstream replay. MariaDB's
current CHANGE MASTER TO documentation explicitly
warns that MASTER_USE_GTID=current_pos can be
unsafe during role transitions if local transactions
contaminated that position; slave_pos keeps the
replication resume point tied to replicated history.
2. Independent GTID lab
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"
docker compose down -vdocker compose up -d
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;
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
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);UPDATE servicehub_replication_lab.tickets SET state='assigned' WHERE ticket_id=1;SELECT @@global.gtid_binlog_pos;
SELECT @@global.gtid_binlog_pos AS local_binlog, @@global.gtid_slave_pos AS replicated, @@global.gtid_current_pos AS current_state;SHOW REPLICA STATUS\G
The replica's gtid_slave_pos should include the
primary's latest domain-14 sequence after catch-up. If
log_slave_updates=ON, replicated events may also
be written to the replica's binlog, but the conceptual meaning
of the variables remains distinct.
3. Domain IDs create independently ordered streams
If every writer in a traditional single-primary hierarchy uses domain 14, that domain describes one logical write stream. Multi-source or deliberately multi-primary designs may use distinct domains so independent streams can be ordered and, in some cases, applied independently. Reusing a domain for two independently writable primaries is not a harmless naming choice: both can generate sequence histories that collide or diverge.
| Design | Domain approach | Reasoning |
|---|---|---|
| single writer + replicas | one controlled domain | simplest linear history |
| multi-source independent datasets | distinct domains per source stream | positions can represent independent ordered streams |
| two active writers touching same data | GTID domains alone do not solve conflicts | application/data conflict control still required |
Do not assign random domains per server merely because the field exists. Document ownership and expected writers for every active domain.
4. GTID strict mode: fail loudly on impossible history
gtid_strict_mode=ON adds safety checks for sequence
histories that would otherwise permit confusing divergence. It
is not required for MariaDB GTID replication to function, but it
makes automated topologies easier to reason about by stopping on
out-of-order or missing-history conditions.
The replica's read_only setting is an application
guardrail, not a magical immutability boundary for privileged
administrators. In the disposable lab, temporarily disable it
and create a local binlogged transaction in the same GTID
domain. This demonstrates why “just fix the replica directly”
can contaminate failover state.
SET GLOBAL read_only=OFF;INSERT INTO servicehub_replication_lab.tickets(ticket_id,customer_id,state)VALUES (900,900,'local-only');SET GLOBAL read_only=ON;SELECT @@global.gtid_binlog_pos, @@global.gtid_slave_pos, @@global.gtid_current_pos;
INSERT INTO servicehub_replication_lab.tickets(ticket_id,customer_id,state)VALUES (901,901,'primary');
SHOW REPLICA STATUS\GSELECT @@global.gtid_binlog_pos, @@global.gtid_slave_pos, @@global.gtid_current_pos;
The exact error text and timing can vary by release and prior GTID state, so diagnose the fields rather than memorizing a string. The important evidence is that local binlogged history changed the replica's logical position. In production, do not paper over this by disabling strict mode until green. Determine whether the local event belongs in authoritative history.
5. Safe repair for the lesson: rebuild, do not rewrite history casually
Because this lab intentionally corrupted role discipline, the clean repair is to destroy and reseed the replica. That mirrors the conservative production principle: when authoritative history is uncertain, a validated rebuild from Chapter 13 is often safer than hand-editing GTID state.
docker compose down -vdocker compose up -d
In a real topology you would normally rebuild only the affected
replica from a trusted primary/backup; the full reset here keeps
the lesson self-contained. For real incidents, capture
SHOW REPLICA STATUS, GTID variables, binlog
inventory, data checks, and application routing before changing
anything. If you must reset gtid_slave_pos, treat
it as a recovery operation with a documented source of truth and
rollback path.
6. Repointing: GTID removes filename coupling, not failover work
Once a replica uses MariaDB GTIDs, it can be pointed to another server that contains the required GTID history without manually translating binlog file/position pairs. The replica requests the next GTID after its saved position. That reduces one class of failover error.
STOP REPLICA;CHANGE MASTER TO MASTER_HOST='candidate-primary', MASTER_PORT=3306, MASTER_USER='repl', MASTER_PASSWORD='rotated-secret', MASTER_USE_GTID=slave_pos;START REPLICA;SHOW REPLICA STATUS\G
Repointing still fails if the candidate primary purged required binlogs, lacks a domain/sequence the replica needs, has divergent history, or should not be trusted as authoritative. GTID is an identity/position mechanism, not quorum or fencing.
7. MariaDB versus MySQL GTID semantics
| Dimension | MariaDB | MySQL family concept |
|---|---|---|
| identifier form | domain-server-sequence |
server UUID/SID plus transaction number |
| domain concept | explicit ordered stream dimension | not the same model |
| resume option | MASTER_USE_GTID=slave_pos/current_pos |
different auto-position syntax/metadata |
| cross-vendor copy/paste | unsafe without exact source/target compatibility testing | |
8. Production judgment
-
Prefer
slave_posfor ordinary replica resume semantics when you want the position to mean “last upstream GTID applied.” - Use strict mode as a guardrail only after understanding legacy/local-write behavior and validating your topology.
-
Keep application/DBA writes off replicas unless they are
explicitly part of the topology design;
read_onlyplus privilege design from Chapter 12 helps. - Document every domain's intended writer(s). A GTID domain is a concurrency/topology boundary, not a label.
- Before failover, compare executed GTID state and prove the candidate has all required committed transactions; after failover, fence the old primary before accepting writes.
Next, use MariaDB's worker pool and commit-order metadata to accelerate apply without assuming that “more threads” means “less lag.”
Check your understanding
- What are the three components of a MariaDB GTID?
- Why can gtid_current_pos differ from gtid_slave_pos on a replica?
- What does gtid_strict_mode protect against?
- Why is a GTID domain a topology decision?
- Does GTID-based repointing remove the need to fence the old primary?
Review the answers
A MariaDB GTID is domain ID, server ID, and sequence number. gtid_current_pos can include local binlogged transactions in addition to replicated history, while gtid_slave_pos tracks applied upstream GTIDs. Strict mode adds checks that stop on unsafe/out-of-order history. Domains define independently ordered streams and therefore affect multi-source/multi-writer semantics. GTIDs simplify position tracking but do not provide fencing, consensus, routing, or zero-data-loss guarantees.