Skip to content

Commit 4adea12

Browse files
committed
fix(replication): wait for replicas to be ready to serve queries after deploy (fixes #131)
After 'deploy replication' (master/slave), the initialize_slaves script now waits until each replica accepts a simple query from the sandbox user before returning. This eliminates the race where the first command on a just-started replica (e.g. ./s1) fails with 'Access denied' or connection errors, especially visible on MySQL 8.4+ and 9.x. - Added wait_until_replica_ready helper (modeled on wait_until_wsrep_ready). - Called after START REPLICA in both init_slaves and init_slaves_84 templates. - Self-contained function in the templates for robustness. - Also added the helper to sb_include.gotxt for reuse by other scripts. Tested via template changes; no behavior change for older versions.
1 parent c097065 commit 4adea12

3 files changed

Lines changed: 65 additions & 0 deletions

File tree

sandbox/templates/replication/init_slaves.gotxt

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,26 @@
66
# This script is called by 'start_all' when needed
77
SBDIR={{.SandboxDir}}
88
cd "$SBDIR"
9+
10+
# Wait until a replica is ready to accept queries from the sandbox user.
11+
# This is called after START REPLICA (or START SLAVE) during replication
12+
# initialization to prevent the race where the first client command on a
13+
# just-started replica fails with "Access denied" or connection errors.
14+
# Observed especially with MySQL 8.4+ and 9.x.
15+
wait_until_replica_ready() {
16+
local use_cmd=${1:-$SBDIR/use}
17+
local max_attempts=${2:-60}
18+
local sleep_sec=${3:-1}
19+
for i in $(seq 1 $max_attempts); do
20+
if $use_cmd -BN -e "SELECT 1;" >/dev/null 2>&1 ; then
21+
return 0
22+
fi
23+
sleep $sleep_sec
24+
done
25+
echo "WARNING: replica not ready to accept queries after $((max_attempts * sleep_sec))s (cmd: $use_cmd)" >&2
26+
return 1
27+
}
28+
929
# workaround for Bug#89959
1030
$SBDIR/{{.MasterLabel}}/use -h {{.MasterIp}} -u {{.RplUser}} -p{{.RplPassword}} -e 'set @a=1'
1131
if [ ! -f needs_initialization ]
@@ -18,6 +38,8 @@ fi
1838
echo "initializing {{.SlaveLabel}} {{.Node}}"
1939
echo '{{.ChangeMasterTo}} {{.MasterHostParam}}="{{.MasterIp}}", {{.MasterPortParam}}={{.MasterPort}}, {{.MasterUserParam}}="{{.RplUser}}", {{.MasterPasswordParam}}="{{.RplPassword}}" {{.MasterAutoPosition}} {{.ChangeMasterExtra}}' | $SBDIR/{{.NodeLabel}}{{.Node}}/use -u root
2040
$SBDIR/{{.NodeLabel}}{{.Node}}/use -u root -e '{{.StartReplica}}'
41+
# Ensure the replica is serving queries before we return from initialization.
42+
wait_until_replica_ready "$SBDIR/{{.NodeLabel}}{{.Node}}/use" 60 1
2143
{{end}}
2244
if [ -x ./post_initialization ]
2345
then

sandbox/templates/replication/init_slaves_84.gotxt

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,26 @@
77
# Uses MySQL 8.4+ replication syntax (CHANGE REPLICATION SOURCE TO)
88
SBDIR={{.SandboxDir}}
99
cd "$SBDIR"
10+
11+
# Wait until a replica is ready to accept queries from the sandbox user.
12+
# This is called after START REPLICA (or START SLAVE) during replication
13+
# initialization to prevent the race where the first client command on a
14+
# just-started replica fails with "Access denied" or connection errors.
15+
# Observed especially with MySQL 8.4+ and 9.x.
16+
wait_until_replica_ready() {
17+
local use_cmd=${1:-$SBDIR/use}
18+
local max_attempts=${2:-60}
19+
local sleep_sec=${3:-1}
20+
for i in $(seq 1 $max_attempts); do
21+
if $use_cmd -BN -e "SELECT 1;" >/dev/null 2>&1 ; then
22+
return 0
23+
fi
24+
sleep $sleep_sec
25+
done
26+
echo "WARNING: replica not ready to accept queries after $((max_attempts * sleep_sec))s (cmd: $use_cmd)" >&2
27+
return 1
28+
}
29+
1030
# workaround for Bug#89959
1131
$SBDIR/{{.MasterLabel}}/use -h {{.MasterIp}} -u {{.RplUser}} -p{{.RplPassword}} -e 'set @a=1'
1232
if [ ! -f needs_initialization ]
@@ -19,6 +39,8 @@ fi
1939
echo "initializing {{.SlaveLabel}} {{.Node}}"
2040
echo '{{.ChangeMasterTo}} {{.MasterHostParam}}="{{.MasterIp}}", {{.MasterPortParam}}={{.MasterPort}}, {{.MasterUserParam}}="{{.RplUser}}", {{.MasterPasswordParam}}="{{.RplPassword}}" {{.MasterAutoPosition}} {{.ChangeMasterExtra}}' | $SBDIR/{{.NodeLabel}}{{.Node}}/use -u root
2141
$SBDIR/{{.NodeLabel}}{{.Node}}/use -u root -e '{{.StartReplica}}'
42+
# Ensure the replica is serving queries before we return from initialization.
43+
wait_until_replica_ready "$SBDIR/{{.NodeLabel}}{{.Node}}/use" 60 1
2244
{{end}}
2345
if [ -x ./post_initialization ]
2446
then

sandbox/templates/single/sb_include.gotxt

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,3 +84,24 @@ function wait_until_wsrep_ready
8484
echo "WARNING: wsrep_ready not ON after $((max_attempts * sleep_sec))s" >&2
8585
return 1
8686
}
87+
88+
# Wait until a replica is ready to accept queries from the sandbox user.
89+
# This is called after START REPLICA (or START SLAVE) during replication
90+
# initialization to prevent the race where the first client command on a
91+
# just-started replica fails with "Access denied" or connection errors.
92+
# Observed especially with MySQL 8.4+ and 9.x.
93+
# Usage: wait_until_replica_ready "$SBDIR/s1/use" 60 1
94+
function wait_until_replica_ready
95+
{
96+
local use_cmd=${1:-$SBDIR/use}
97+
local max_attempts=${2:-60}
98+
local sleep_sec=${3:-1}
99+
for i in $(seq 1 $max_attempts); do
100+
if $use_cmd -BN -e "SELECT 1;" >/dev/null 2>&1 ; then
101+
return 0
102+
fi
103+
sleep $sleep_sec
104+
done
105+
echo "WARNING: replica not ready to accept queries after $((max_attempts * sleep_sec))s (cmd: $use_cmd)" >&2
106+
return 1
107+
}

0 commit comments

Comments
 (0)