Build a complete HA database architecture demonstrating the full data flow: write to MySQL primary → ProxySQL routes reads/writes → Redis caches hot data → Elasticsearch indexes for search. This integrates all concepts from Labs 01-19.
docker network create ha-demo-net
echo "=== Starting MySQL Primary ==="
docker run -d \
--name ha-mysql-primary \
--network ha-demo-net \
--hostname ha-mysql-primary \
-e MYSQL_ROOT_PASSWORD=rootpass \
-e MYSQL_DATABASE=catalog \
mysql:8.0 \
--server-id=1 \
--log-bin=mysql-bin \
--binlog-format=ROW \
--gtid-mode=ON \
--enforce-gtid-consistency=ON
echo "=== Starting MySQL Replica ==="
docker run -d \
--name ha-mysql-replica \
--network ha-demo-net \
--hostname ha-mysql-replica \
-e MYSQL_ROOT_PASSWORD=rootpass \
mysql:8.0 \
--server-id=2 \
--log-bin=mysql-bin \
--binlog-format=ROW \
--gtid-mode=ON \
--enforce-gtid-consistency=ON \
--read-only=ON
echo "=== Starting Redis Cache ==="
docker run -d \
--name ha-redis \
--network ha-demo-net \
--hostname ha-redis \
redis:7 \
redis-server --maxmemory 128mb --maxmemory-policy allkeys-lru
echo "=== Starting Elasticsearch ==="
docker run -d \
--name ha-elasticsearch \
--network ha-demo-net \
--hostname ha-elasticsearch \
-e "discovery.type=single-node" \
-e "xpack.security.enabled=false" \
-e "ES_JAVA_OPTS=-Xms512m -Xmx512m" \
-p 9200:9200 \
elasticsearch:8.11.0
echo "All containers starting..."
# Wait for MySQL primary
for i in $(seq 1 30); do docker exec ha-mysql-primary mysql -uroot -prootpass -e "SELECT 1" 2>/dev/null && break || sleep 2; done
echo "MySQL Primary ready"
# Wait for MySQL replica
for i in $(seq 1 30); do docker exec ha-mysql-replica mysql -uroot -prootpass -e "SELECT 1" 2>/dev/null && break || sleep 2; done
echo "MySQL Replica ready"
# Wait for Elasticsearch
for i in $(seq 1 30); do curl -s http://localhost:9200/_cluster/health 2>/dev/null | grep -q status && break || sleep 3; done
echo "Elasticsearch ready"
=== Starting MySQL Primary ===
=== Starting MySQL Replica ===
=== Starting Redis Cache ===
=== Starting Elasticsearch ===
All containers starting...
MySQL Primary ready
MySQL Replica ready
Elasticsearch ready
# Create replication user on primary
docker exec ha-mysql-primary mysql -uroot -prootpass -e "
CREATE USER 'repl'@'%' IDENTIFIED WITH mysql_native_password BY 'replpass';
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%';
CREATE USER 'appuser'@'%' IDENTIFIED WITH mysql_native_password BY 'apppass';
GRANT ALL ON catalog.* TO 'appuser'@'%';
CREATE USER 'monitor'@'%' IDENTIFIED WITH mysql_native_password BY 'monitor';
GRANT REPLICATION CLIENT ON *.* TO 'monitor'@'%';
FLUSH PRIVILEGES;
"
# Connect replica to primary
PRIMARY_IP=$(docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' ha-mysql-primary)
docker exec ha-mysql-replica mysql -uroot -prootpass <<EOF
CHANGE MASTER TO
MASTER_HOST='${PRIMARY_IP}',
MASTER_USER='repl',
MASTER_PASSWORD='replpass',
MASTER_AUTO_POSITION=1;
START SLAVE;
EOF
sleep 3
# Verify replication
docker exec ha-mysql-replica mysql -uroot -prootpass -e "
SHOW SLAVE STATUS\G
" | grep -E "Slave_IO_Running|Slave_SQL_Running|Seconds_Behind_Master"
echo "=== HIGH-AVAILABILITY TEST ==="
# Record state before failure
echo "Products before failover:"
docker exec ha-mysql-primary mysql -uroot -prootpass catalog -e "
SELECT COUNT(*) AS count FROM products;
"
# Simulate primary failure
echo ""
echo "Stopping MySQL primary (simulating failure)..."
docker stop ha-mysql-primary
sleep 5
echo ""
echo "=== After Primary Failure ==="
# Redis still serves cached data
echo "Redis still has cached product:"
docker exec ha-redis redis-cli GET "product:6" | python3 -c "
import sys, json
data = sys.stdin.read().strip()
if data and data != '(nil)':
p = json.loads(data)
print(f' Redis serving: {p[\"name\"]} @ \${p[\"price\"]} (cache still hot!)')
else:
print(' Cache miss/expired')
"
# Elasticsearch still searches
echo ""
echo "Elasticsearch still searchable:"
curl -s "localhost:9200/products/_search?q=headphones&size=2" | python3 -c "
import sys,json
d=json.load(sys.stdin)
print(f' ES returned {d[\"hits\"][\"total\"][\"value\"]} results (ES unaffected by MySQL failure!)')
"
# Replica can be promoted
echo ""
echo "Promoting replica to primary..."
docker exec ha-mysql-replica mysql -uroot -prootpass -e "
STOP SLAVE;
RESET SLAVE ALL;
SET GLOBAL read_only = OFF;
SET GLOBAL super_read_only = OFF;
"
# Test write on promoted replica
docker exec ha-mysql-replica mysql -uroot -prootpass catalog -e "
INSERT INTO products (sku, name, category, brand, price, stock)
VALUES ('POST-FAIL', 'Post-Failover Product', 'test', 'TestBrand', 9.99, 1);
SELECT 'Write on promoted replica successful!', COUNT(*) AS total FROM products;
"
echo ""
echo "=== Recovery Complete ==="
=== HIGH-AVAILABILITY TEST ===
Products before failover:
+-------+
| count |
+-------+
| 6 |
+-------+
Stopping MySQL primary (simulating failure)...
=== After Primary Failure ===
Redis still has cached product:
Redis serving: Bose QuietComfort Ultra @ $429.99 (cache still hot!)
Elasticsearch still searchable:
ES returned 3 results (ES unaffected by MySQL failure!)
Promoting replica to primary...
Write on promoted replica successful!
+-------+
| total |
+-------+
| 7 |
+-------+
=== Recovery Complete ===
echo "=== ARCHITECTURE EVALUATION ==="
cat << 'EOF'
┌─────────────────────────────────────────────────────────────────┐
│ COMPONENT RESPONSIBILITIES │
├──────────────────┬──────────────────────────────────────────────┤
│ MySQL Primary │ Authoritative writes, ACID transactions │
│ MySQL Replica │ Read offloading, failover target │
│ ProxySQL │ Connection pooling, read/write splitting │
│ Redis │ Cache layer, session storage, rate limiting │
│ Elasticsearch │ Full-text search, aggregations, analytics │
└──────────────────┴──────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ FAILURE SCENARIOS │
├──────────────────┬──────────────────────────────────────────────┤
│ MySQL Primary ↓ │ Promote replica, Redis/ES serve reads │
│ MySQL Replica ↓ │ All reads redirect to primary │
│ Redis ↓ │ All reads go to MySQL (higher latency) │
│ Elasticsearch ↓ │ Basic reads still work, search unavailable │
│ ProxySQL ↓ │ Direct connection to MySQL (bypass proxy) │
└──────────────────┴──────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ PERFORMANCE CHARACTERISTICS │
├──────────────────┬──────────────────────────────────────────────┤
│ Cache hit │ < 1ms (Redis) │
│ Cache miss │ 1-10ms (MySQL via ProxySQL) │
│ Search query │ 1-50ms (Elasticsearch) │
│ Write path │ 5-20ms (MySQL + cache invalidation + ES index)│
└──────────────────┴──────────────────────────────────────────────┘
CONSISTENCY MODEL:
MySQL Primary → Replica: Eventual (async replication, ~0ms lag)
Redis cache: Invalidation on write (cache-aside pattern)
Elasticsearch: Near real-time (indexed within 1 second of write)
EOF
# Final cleanup
docker stop ha-mysql-primary ha-mysql-replica ha-redis ha-elasticsearch 2>/dev/null
docker rm -f ha-mysql-primary ha-mysql-replica ha-redis ha-elasticsearch
docker network rm ha-demo-net
rm -f /tmp/full_data_flow.py /tmp/sync_to_es.py
echo ""
echo "╔═══════════════════════════════════════════════════════╗"
echo "║ CAPSTONE LAB COMPLETE! ║"
echo "║ ║"
echo "║ You've built and tested a production-ready HA ║"
echo "║ database architecture integrating: ║"
echo "║ • MySQL replication (Labs 01-03) ║"
echo "║ • Query optimization (Labs 06-09) ║"
echo "║ • Connection pooling via ProxySQL (Lab 10) ║"
echo "║ • Redis caching layer (Lab 17) ║"
echo "║ • Elasticsearch search (Lab 19) ║"
echo "╚═══════════════════════════════════════════════════════╝"