Chapter 22 · Production Capstone: Design, Cluster, Secure, Tune, and Recover MariaDB
Run Restore, Failover, Split-Brain, Performance, and Corruption Drills—and Defend the Design
Inject safe failures into the disposable capstone, measure detection and recovery, prove data correctness, exercise restore/failover/divergence/load/configuration drills, and defend the final MariaDB operating model with prioritized next improvements.
Learning outcomes
The final lesson is a production game-day. Every failure is injected only into the disposable capstone, and every drill has four outputs: detection time, recovery time, data-correctness evidence, and the SLO/RPO/RTO result. The design is accepted only to the extent the evidence supports it.
Run process/node-loss and async failover drills with explicit fencing and client pool refresh.
Create and detect divergent writes during an isolated replica partition, then recover by choosing one authority and rebuilding rather than merging blindly.
Exercise blocked transactions, accidental DML, backup corruption/missing components, configuration failure and load spikes safely.
Record RPO/RTO and correctness using checksums, invariants, GTID/binlog evidence and application outcomes.
Defend the final architecture, name residual risks and prioritize the next evidence-driven improvements.
Do not execute network partitions, process kills, datadir changes, backup-file corruption or conflicting writes against production. The commands below assume the disposable local capstone topology from Lesson 4 or an equivalent isolated environment.
1. Build a game-day scorecard before breaking anything
drill_id,start_utc,detected_utc,recovered_utc,target_rpo_s,target_rto_s,actual_data_loss_s,actual_recovery_s,correctness_pass,slo_pass,evidence_uri,notesRules: start/detected/recovered use one synchronized clock source “correctness_pass” requires named invariants, not “site loads” preserve logs/status/GTID/checksum evidence before cleanup failure that exceeds target is useful evidence, not a reason to hide the run
Detection time is separate from recovery time. A system that recovers in one minute after an operator notices the problem 40 minutes later does not meet a 30-minute RTO from user impact.
2. Drill A — process/node loss and controlled async failover
primary: writable authority, server_id=221replica: read_only=ON, server_id=222, GTID replication healthyapplication: write endpoint points only to primaryprecondition: SHOW REPLICA STATUS confirms candidate has applied the test transactionbackup/PITR: independent recovery path still exists
# Record evidence before failure.docker compose exec replica mariadb -uroot -pdisposable-root -e "SHOW REPLICA STATUS\G"# Kill only the disposable primary.docker compose stop primary# Do NOT route writes yet. First record replica state and fence/confirm old primary is stopped.docker compose exec replica mariadb -uroot -pdisposable-root -e "SELECT @@server_id,@@read_only,@@global.gtid_slave_pos,@@global.gtid_binlog_pos;"# Promote the chosen replica only after the old writer is fenced.docker compose exec replica mariadb -uroot -pdisposable-root -e "STOP REPLICA; SET GLOBAL read_only=OFF;"# Refresh/recreate application pools so no stale connections point at the dead writer.
The exact production promotion procedure depends on topology, GTID state, routing and MariaDB version. The learning invariant is universal: fence first, prove candidate state, then route. Do not erase replication metadata immediately; it is incident evidence and may be needed for rejoin planning.
3. Drill B — network partition and deliberate divergent writes
Async replication has no quorum mechanism that automatically prevents both sides from being made writable. This drill demonstrates the “split-brain” risk created by operator/routing error.
# Recreate/reset the original primary+replica topology before this drill.docker network disconnect servicehub22_net servicehub22-replica# On the isolated replica ONLY, deliberately remove the guardrail for the exercise:docker exec servicehub22-replica mariadb -uroot -pdisposable-root -e "SET GLOBAL read_only=OFF; INSERT INTO servicehub22.outbox_event(tenant_id,aggregate_type,aggregate_id,event_type,payload) VALUES(1,'ticket',5001,'WRONG_SIDE_WRITE','{}');"# Independently write a different event on the real primary.docker compose exec primary mariadb -uroot -pdisposable-root -e "INSERT INTO servicehub22.outbox_event(tenant_id,aggregate_type,aggregate_id,event_type,payload) VALUES(1,'ticket',5001,'PRIMARY_WRITE','{}');"# Reconnect network only after collecting GTID/data evidence.docker network connect servicehub22_net servicehub22-replica
Do not try to “merge GTIDs until green.” Two writable histories now exist. Choose the authoritative history according to the incident decision, preserve evidence, and rebuild the non-authoritative node from a trusted seed/backup. The drill justifies fencing and routing controls far better than a diagram does.
4. Drill C — blocked transaction and lock storm diagnosis
USE servicehub22;START TRANSACTION;UPDATE ticket SET subject='held by drill A'WHERE tenant_id=1 AND ticket_id=5001;-- Keep the transaction open temporarily; do not COMMIT yet.
SET SESSION innodb_lock_wait_timeout=10;UPDATE servicehub22.ticket SET priority=5WHERE tenant_id=1 AND ticket_id=5001;
SHOW PROCESSLIST;SELECT * FROM information_schema.INNODB_TRX\GSHOW ENGINE INNODB STATUS\G
The repair is to identify the blocking transaction and business
operation, then choose whether to commit/rollback/terminate it.
Randomly killing waiters treats the symptom. End the drill with
ROLLBACK in Session A and verify Session B’s
outcome.
5. Drill D — accidental DML and PITR boundary
SELECT NOW(6) AS before_bad_delete;SELECT COUNT(*) AS before_count FROM servicehub22.ticket WHERE tenant_id=1;-- Disposable incident injection.DELETE FROM servicehub22.ticketWHERE tenant_id=1 AND status='CLOSED';SELECT NOW(6) AS after_bad_delete;SELECT COUNT(*) AS after_count FROM servicehub22.ticket WHERE tenant_id=1;
Do not “undo” by guessing which rows existed. Use the Lesson 4 backup + retained binlogs to restore an isolated target and replay only to the transaction boundary before the destructive commit. Compare counts and named business rows. After proving recovery, decide whether production recovery would be full restore, selective logical extraction, or another controlled method.
6. Drill E — detect a corrupt/missing backup component without destroying the good copy
cp -a /tmp/capstone22-full /tmp/capstone22-brokenfind /tmp/capstone22-full -type f -print0 | sort -z | xargs -0 sha256sum > /tmp/capstone22-full.sha256# Damage one disposable copy. Choose an ordinary data file inside the copied backup.TARGET=$(find /tmp/capstone22-broken -type f -name '*.ibd' | head -n 1)printf 'broken' > "$TARGET"# A manifest built for the GOOD tree can be adapted to compare paths, or generate paired manifests.find /tmp/capstone22-broken -type f -print0 | sort -z | xargs -0 sha256sum > /tmp/capstone22-broken.sha256diff -u /tmp/capstone22-full.sha256 /tmp/capstone22-broken.sha256 || true# Never use the broken copy for recovery. Preserve it only as drill evidence, then delete it.
Cryptographic checksums detect change; they do not prove semantic restorability. The stronger control is checksum + prepare/restore + database-level invariant checks.
7. Drill F — configuration failure and startup evidence
# No production config file is edited.docker run --rm --name mariadb22-bad-config -e MARIADB_ROOT_PASSWORD=disposable-root mariadb:12.3.2 --definitely-not-a-real-option=1# Expected: server startup fails and logs identify an unknown/invalid option.# Capture the exit code and logs as evidence.
The lesson from a config failure is not merely “fix the typo.” Production change should have syntax/startup validation, canary rollout, previous config retention and a rollback path that does not require discovering the old value during outage.
8. Drill G — load spike and SLO/headroom result
# Baseline first; then increase only one demand variable.CONCURRENCY=8 ITERATIONS=100 node bench.mjs > baseline.jsonCONCURRENCY=32 ITERATIONS=100 node bench.mjs > spike.json# Capture server/OS evidence over the same windows.# Do not claim 32 is “high” universally; it is just this drill's demand multiplier.
Record whether throughput rises, flattens or falls while p95/p99 and errors change. The capacity knee is workload/hardware-specific. Headroom policy should keep expected bursts away from the region where latency accelerates and errors begin.
9. Correctness verification after every drill
-- No cross-tenant foreign-key violations can exist if constraints are intact.SELECT COUNT(*) AS tickets_missing_customerFROM servicehub22.ticket tLEFT JOIN servicehub22.customer c ON c.tenant_id=t.tenant_id AND c.customer_id=t.customer_idWHERE c.customer_id IS NULL;SELECT COUNT(*) AS comments_missing_ticketFROM servicehub22.ticket_comment cLEFT JOIN servicehub22.ticket t ON t.tenant_id=c.tenant_id AND t.ticket_id=c.ticket_idWHERE t.ticket_id IS NULL;-- Record selected business counts and checksums appropriate to your dataset.SELECT tenant_id,status,COUNT(*)FROM servicehub22.ticketGROUP BY tenant_id,statusORDER BY tenant_id,status;CHECKSUM TABLE servicehub22.tenant, servicehub22.customer, servicehub22.ticket, servicehub22.ticket_comment;
CHECKSUM TABLE is supplementary evidence, not a
universal cross-version/cross-engine business-proof mechanism.
Application invariants and source-of-truth comparisons are still
required.
10. Defend the design: evidence, residual risk, next improvements
| Decision | Evidence earned in capstone | Residual risk / revisit trigger |
|---|---|---|
| InnoDB relational model | FK/check/index tests + transaction workload | schema growth and tenant skew may require lifecycle redesign |
| Async GTID replica | lag/breakage/failover drill | possible data-loss window; external fencing/routing remains required |
| Physical backup + binlog PITR | prepare/restore/replay drill | retention, offsite failure domain and restore automation need continuous testing |
| Least privilege + TLS contract | grants + TLS/session checks | secret rotation/certificate lifecycle are operational dependencies |
| Evidence-driven tuning | repeatable before/after benchmark | future workload shape may invalidate current index/config choices |
| Upgrade gate | version/config/backup/canary checklist | downgrade may require restore/rebuild; plugin/topology compatibility evolves |
1. Workload and SLOs exact user journeys, percentiles, availability, RPO/RTO, growth, retention2. Data correctness schema invariants, tenant isolation, transactions, idempotency3. Capacity/performance representative workload, bottleneck evidence, headroom and triggers4. Security identities/roles, TLS verification, secrets, admin separation5. HA vs DR write authority, replica/Galera choice, fencing/routing, backups/PITR6. Observability user-impact signals, DB/OS evidence, alert thresholds and ownership7. Change management migrations, config inventory, upgrade gate, canary, rollback boundary8. Game-day results each drill's detection, RPO, RTO, correctness and failed assumptions9. Residual risks what is consciously NOT solved10. Next 90-day priorities highest risk-reduction work with measurable exit criteria
11. Final checks and course completion
Check your reasoning
- Why rebuild the non-authoritative node after divergent async writes?
- Why record detection time separately from recovery time?
- What does a successful checksum fail to prove?
- Why is a failed drill valuable?
- What is the capstone’s central operating principle?
Review the answers
-
Because two independent histories exist; forcing replication “green” can hide lost/conflicting data. Choose one authority and reseed the other from trusted state.
-
RTO is experienced from impact; slow detection can violate it even when the technical repair itself is fast.
-
That all business invariants are correct, that the chosen recovery point is right, or that every application-visible row is semantically correct.
-
It reveals that an architecture assumption or runbook does not meet the stated SLO/RPO/RTO, creating actionable evidence before a real incident.
-
Define a measurable contract, observe actual state, change one thing deliberately, verify recovery/correctness, and keep residual risk explicit.
You now have the pieces of a MariaDB production operating model: architecture record, schema and privileges, application contract, evidence-driven tuning, backup/PITR, HA, observability, upgrade gates and failure-drill results. Production readiness is not permanent; schedule recurring restore, failover, performance and upgrade rehearsals as the workload and MariaDB release state change.