Skip to content

Commit 36cfdec

Browse files
committed
Add a task to reset a MySQL slave
Add a task to reset a MySQL slave by running `RESET SLAVE;` and repointing the replication log to the last known position. I found these steps very useful in recovering MySQL replication after the VMs running master and slaves had been powered off in the new Carrenza Production environment and replication was failing when the VMs were turned back on. Specifically, the error I saw was that the slave was 'NULL' seconds behind the MySQL master. I've used `run()` in combination with `sudo -i` rather than `sudo()` so that the `HOME` environment variable is set to `/root` so that `/root/my.cnf` is picked up by the MySQL client so that it does not prompt for a password. I've used `run('sudo -i...')` rather than `run_mysql_command()` when I've needed to grep for values. I tried using a pure SQL query to retrieve the replication status. `SELECT foo FROM information_schema.GLOBAL_STATUS;` didn't work because neither the `information_schema` or `mysql` databases store the values this task uses. I also tried `SHOW STATUS LIKE 'Slave_Running'`, but couldn't find a way (even using subqueries) to pull out just the value I needed.
1 parent 5cc21ee commit 36cfdec

File tree

1 file changed

+50
-1
lines changed

1 file changed

+50
-1
lines changed

mysql.py

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,4 +68,53 @@ def setup_slave_from_master(master):
6868
run_mysql_command("START SLAVE")
6969
run_mysql_command("SET GLOBAL slow_query_log=ON")
7070

71-
run_mysql_command("SHOW SLAVE STATUS\G")
71+
slave_status()
72+
73+
74+
@task
75+
def reset_slave():
76+
"""
77+
Used to reset a slave if MySQL replication is failing
78+
79+
If you see that the slave is 'NULL' seconds behind the master,
80+
the problem may be resolved by running this task.
81+
82+
See docs on 'RESET SLAVE':
83+
https://dev.mysql.com/doc/refman/5.5/en/reset-slave.html
84+
"""
85+
86+
# Confirm slave status in case we need to refer to the values later
87+
slave_status()
88+
run_mysql_command("STOP SLAVE;")
89+
90+
with hide('everything'):
91+
# Store last known log file and position
92+
master_log_file = run("sudo -i mysql -e 'SHOW SLAVE STATUS\G' | grep '^\s*Relay_Master_Log_File:' | awk '{ print $2 }'")
93+
master_log_pos = run("sudo -i mysql -e 'SHOW SLAVE STATUS\G' | grep '^\s*Exec_Master_Log_Pos:' | awk '{ print $2 }'")
94+
95+
if not master_log_file or not master_log_pos:
96+
abort("Failed to determine replication log file and position, aborting.")
97+
98+
# Forget log file and position
99+
run_mysql_command("RESET SLAVE;")
100+
101+
# Repoint log file and position to last known values
102+
run_mysql_command("CHANGE MASTER TO MASTER_LOG_FILE='{}', MASTER_LOG_POS={};" \
103+
.format(master_log_file, master_log_pos))
104+
run_mysql_command("START SLAVE;")
105+
106+
with hide('everything'):
107+
seconds_behind_master = run("sudo -i mysql -e 'SHOW SLAVE STATUS\G' | grep '^\s*Seconds_Behind_Master:' | awk '{ print $2 }'")
108+
109+
# Compare as a string to ensure we got a non-nil value from MySQL
110+
if seconds_behind_master != '0':
111+
abort("Slave is still behind master by {} seconds; run mysql.slave_status to check status" \
112+
.format(seconds_behind_master))
113+
114+
115+
@task
116+
def slave_status():
117+
"""
118+
Show status of MySQL replication on slave; must be run against the slave host
119+
"""
120+
run_mysql_command("SHOW SLAVE STATUS\G;")

0 commit comments

Comments
 (0)