Graceful MySQLD control — move_sql.sh
move_sql.sh is BOA's graceful MySQLD orchestrator. Despite the name it does
not move databases between hosts and has no per-site logic. It cleanly
stops, starts, or restarts the local Percona/MySQL server, preparing InnoDB for
a quick, dirty-page-free shutdown first.
Its verbs fall into two classes — and the difference matters. stop and
restart are whole-stack: they take Nginx and every PHP-FPM pool down with
the database, which is what on-disk surgery needs and what an operator asking
for a clean bounce expects. dbrestart and start are database-only and
never touch the web tier; dbrestart is the verb the auto-healing watchdog
drives, so a database fault heals the database and nothing else.
It exists so BOA's own InnoDB log-resize and Percona upgrade/restart paths can bring the DB server down and back up without losing time on a slow shutdown or a stale socket. Operators rarely run it by hand.
Whole-host or per-Octopus database migration is not provided by this script. For whole-host migration use
xmass; for per-Octopus migration usexoct. See Migration & cloning.
The script lives at /var/xdrago/move_sql.sh.
Invocation
Its only interface is a single positional verb:
/var/xdrago/move_sql.sh restart # whole-stack: stop then start (also the default)
/var/xdrago/move_sql.sh stop # whole-stack graceful shutdown
/var/xdrago/move_sql.sh start # start + wait for the socket (DB only)
/var/xdrago/move_sql.sh dbrestart # restart the DB only, never the web tier
/var/xdrago/move_sql.sh # no argument -> restart
There are no flags — no --site, no --target-db-host, no --help. Any
unrecognised argument falls through to restart. On a proxy host
(/root/.proxy.cnf present) the script exits immediately and does nothing.
What stop does
stop brings the whole web/PHP/DB stack down so MySQLD can shut down cleanly:
1. Create lock pidfiles under /run (see "Locks" below).
2. Stop Nginx, wait for it to exit, then killall nginx.
3. Stop every PHP-FPM instance (PHP 8.5 … 5.3), rotating each
php<NN>-fpm-error.log into /var/backups/php-logs/<timestamp>/,
then pkill -9 any survivors.
4. Prepare InnoDB for a fast, clean shutdown:
SET GLOBAL innodb_max_dirty_pages_pct = 0;
SET GLOBAL innodb_change_buffering = 'none';
SET GLOBAL innodb_buffer_pool_dump_at_shutdown = 1;
SET GLOBAL innodb_io_capacity = 3000;
SET GLOBAL innodb_io_capacity_max = 6000;
On Percona 5.7 only, it also forces an immediate buffer-pool dump
(innodb_buffer_pool_dump_pct = 100; innodb_buffer_pool_dump_now = ON;).
5. SET GLOBAL innodb_fast_shutdown = 1; then `service mysql stop`.
6. Remove the stale /run/mysqld/mysql* sockets.
7. Wait until no mysqld process remains, then remove the locks.
Stopping Nginx and all PHP-FPM first is deliberate: it removes the clients that
would keep writing to the database, so the dirty-page flush converges quickly
and the shutdown stays fast. The InnoDB statements use the root client
implicitly via /root/.my.cnf — no password file is read.
What start does
start is the inverse, kept minimal:
1. Create lock pidfiles under /run.
2. If mysqld is already running, do nothing and exit.
3. Remove any stale /run/mysqld/mysql* sockets.
4. `service mysql start`.
5. Loop until both a mysqld process and /run/mysqld/mysqld.sock
exist ("Waiting for MySQLD graceful start…"), for at most ~60 s.
6. Remove the locks.
The wait in step 5 is bounded. A start that cannot succeed — corrupt data, a full disk, a rejected directive — used to spin for ever while holding the restart locks, which blocked every later auto-heal on the host. It now gives up after roughly a minute with "MySQLD did not come up within ~60s", releases the locks and reports the failure, leaving the next monitor tick free to retry.
It does not start Nginx or PHP-FPM back up — the caller (or BOA's normal
monitor cycle) brings the web tier back. start is purely about the DB server
and its socket.
What restart does
restart (the default) chains the two: _stop_sql then _start_sql, sharing
one set of locks, then exits. This is the form BOA uses when it just needs the
DB server bounced after a config change.
What dbrestart does
dbrestart restarts the database and nothing else. It exists so the auto-healing
watchdog can recover a wedged mysqld without the blast radius of the whole-stack
verbs — no Nginx stop, no PHP-FPM stop, no cache eviction.
1. Create lock pidfiles under /run.
2. If mysqld is already gone, there is nothing to stop.
3. `service mysql stop`, capped at 60 s so a hung server cannot block for ever.
4. Still not answering? TERM the survivor, wait, then KILL it.
5. Clear /run/mysqld/mysql* only once mysqld is genuinely gone.
6. Start the server again through the same `start` path as above.
Steps 3 and 4 re-probe the server between every escalation step and stop the
moment it answers. That is what makes the verb safe on a host where
mysqld_safe supervises Percona: the supervisor respawns a dead mysqld within
a second or two, and hard-killing or de-socketing that fresh, healthy process
would restart the very fault the escalation was trying to clear. A server that
has come back on its own is reported and left alone.
Locks
While running, the script creates four marker pidfiles under /run, which other
BOA components honour as "the DB is being restarted, hold off":
/run/boa_wait.pid/run/fmp_wait.pid/run/restarting_fmp_wait.pid/run/mysql_restart_running.pid
If mysql_restart_running.pid already exists when the script starts, it assumes
another restart is in progress, prints "MySQLD restart procedure in progress?"
and exits without touching anything — so two restarts never race. All four
markers are removed on completion.
The lock cycle deliberately does not drop the page, dentry and inode cache. Evicting the host-wide cache on every database restart forces cold re-reads across every site at once, and that stampede is one of the things that turns a brief database blip into a long, load-driven outage. Genuine memory pressure is the OOM path's job — not a side effect of restarting MySQL.
Where BOA calls it
move_sql.sh is plumbing: several BOA components call it to bounce the DB server
cleanly, but the surrounding logic — not move_sql.sh — owns whatever they are
doing. The stop/start verbs bracket on-disk surgery; the bare restart is
used wherever a plain clean bounce is enough.
- InnoDB log resize (
lib/functions/sql.sh.inc,_innodb_log_file_size_update): callsmove_sql.sh stop, swaps theib_logfile*/ redo-log files and editsinnodb_log_file_size/innodb_redo_log_capacityin/etc/mysql/my.cnf, thenmove_sql.sh start. - Post-
my.cnfrestart: a baremove_sql.sh(default restart) bounces the server after amy.cnfupdate on the Percona setup/upgrade path. - The database watchdog (
monitor/check/mysql.sh): never the whole-stack verbs. Once it has confirmed the server genuinely will not answer, it callsmove_sql.sh dbrestartifmysqldis present but wedged, ormove_sql.sh startifmysqldis gone. A server that answers at all is left alone, so neither aToo many connectionsflood nor high load reaches this path. - Other self-healing callers (
monitor/check/system.sh's OOM cascade,mysql_backup.sh): still call the bare, whole-stackmove_sql.sh. - Whole-host migration (
xmass): the migration tool runsmove_sql.sh restartlocally andstop/startover SSH on the target to checkpoint each end cleanly. Note the direction —move_sql.shdoes no migration itself; it is only the clean-restart building blockxmasscalls.
Where these paths bracket on-disk changes, the surrounding BOA code sets
innodb_fast_shutdown and synchronises directives first; move_sql.sh only
performs the clean stop/start.
Manual use
There is rarely a reason to run it directly, but it is safe when you need a clean DB bounce — for example before a manual InnoDB redo-log change, or to recover from a server that left a stale socket behind:
bash /var/xdrago/move_sql.sh restart # clean restart of the local MySQLD
bash /var/xdrago/move_sql.sh stop # bring the DB down for offline maintenance
# … do maintenance on /var/lib/mysql …
bash /var/xdrago/move_sql.sh start # … then back up
Remember that stop also stops Nginx and all PHP-FPM instances, so the whole
stack is offline between a manual stop and the next start (or until BOA's
monitor restarts the web tier). Do not run stop on its own and walk away from
a production box.
Related
- Percona install + tuning — install + tuning; the
InnoDB log-resize and Percona-upgrade paths that drive
move_sql.sh. - my.cnf lifecycle + mycnfup —
my.cnfupdates that trigger a DB restart. - DB GUI tools — DB inspection tools.
- Migration & cloning — the actual migration
tooling (
xmasswhole-host,xoctper-Octopus) thatmove_sql.shonly checkpoints for.