Skip to content

ci: add ts replication test suite (5.7, 8.0, 8.4, 9.5) #143

ci: add ts replication test suite (5.7, 8.0, 8.4, 9.5)

ci: add ts replication test suite (5.7, 8.0, 8.4, 9.5) #143

name: Integration Tests
on:
push:
branches: [master]
pull_request:
branches: [master]
schedule:
# Run nightly at 2am UTC
- cron: '0 2 * * *'
workflow_dispatch:
jobs:
sandbox-test:
name: Sandbox (${{ matrix.mysql-version }})
runs-on: ubuntu-22.04
strategy:
fail-fast: false
matrix:
mysql-version:
- '8.0.42'
- '8.4.4'
- '9.1.0'
- '9.5.0'
env:
GO111MODULE: on
SANDBOX_BINARY: ${{ github.workspace }}/opt/mysql
MYSQL_VERSION: ${{ matrix.mysql-version }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: '1.22'
- name: Install system libraries
run: |
sudo apt-get update
sudo apt-get install -y libaio1 libnuma1 libncurses5
- name: Build dbdeployer
run: go build -o dbdeployer .
- name: Cache MySQL tarball
uses: actions/cache@v4
with:
path: /tmp/mysql-tarball
key: mysql-${{ matrix.mysql-version }}-linux-x86_64-v1
- name: Download MySQL
run: |
SHORT_VER=$(echo "$MYSQL_VERSION" | grep -oP '^\d+\.\d+')
TARBALL="mysql-${MYSQL_VERSION}-linux-glibc2.17-x86_64.tar.xz"
mkdir -p /tmp/mysql-tarball
if [ ! -f "/tmp/mysql-tarball/$TARBALL" ]; then
echo "Downloading $TARBALL..."
curl -L -f -o "/tmp/mysql-tarball/$TARBALL" \
"https://dev.mysql.com/get/Downloads/MySQL-${SHORT_VER}/$TARBALL" \
|| curl -L -f -o "/tmp/mysql-tarball/$TARBALL" \
"https://downloads.mysql.com/archives/get/p/23/file/$TARBALL"
fi
ls -lh "/tmp/mysql-tarball/$TARBALL"
- name: Unpack MySQL
run: |
mkdir -p "$SANDBOX_BINARY"
TARBALL="mysql-${MYSQL_VERSION}-linux-glibc2.17-x86_64.tar.xz"
./dbdeployer unpack "/tmp/mysql-tarball/$TARBALL" \
--sandbox-binary="$SANDBOX_BINARY"
- name: Test single sandbox
run: |
./dbdeployer deploy single "$MYSQL_VERSION" \
--sandbox-binary="$SANDBOX_BINARY"
~/sandboxes/msb_*/use -e "SELECT VERSION()"
./dbdeployer delete all --skip-confirm
- name: Test replication sandbox
run: |
./dbdeployer deploy replication "$MYSQL_VERSION" \
--sandbox-binary="$SANDBOX_BINARY"
~/sandboxes/rsandbox_*/check_slaves
~/sandboxes/rsandbox_*/test_replication
./dbdeployer delete all --skip-confirm
- name: Cleanup
if: always()
run: |
./dbdeployer delete all --skip-confirm 2>/dev/null || true
pkill -9 -u "$USER" mysqld 2>/dev/null || true
# Run the ts (testscript) replication suite against multiple MySQL versions.
# This tests replication, replication-gtid, and semisync across versions.
ts-replication-test:
name: ts Replication Tests
runs-on: ubuntu-22.04
env:
GO111MODULE: on
SANDBOX_BINARY: ${{ github.workspace }}/opt/mysql
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: '1.22'
- name: Install system libraries
run: |
sudo apt-get update
sudo apt-get install -y libaio1 libnuma1 libncurses5
- name: Build dbdeployer
run: go build -o dbdeployer .
- name: Download MySQL versions
run: |
mkdir -p "$SANDBOX_BINARY"
for VER in 5.7.44 8.0.42 8.4.8 9.5.0; do
SHORT="${VER%.*}"
TARBALL="mysql-${VER}-linux-glibc2.17-x86_64-minimal.tar.xz"
echo "Downloading $VER..."
curl -L -f -s -o "/tmp/$TARBALL" \
"https://dev.mysql.com/get/Downloads/MySQL-${SHORT}/$TARBALL" \
|| curl -L -f -s -o "/tmp/$TARBALL" \
"https://downloads.mysql.com/archives/get/p/23/file/$TARBALL"
./dbdeployer unpack "/tmp/$TARBALL" --sandbox-binary="$SANDBOX_BINARY"
rm -f "/tmp/$TARBALL"
done
echo "Available versions:"
ls "$SANDBOX_BINARY"
- name: Run ts replication tests
env:
TEST_SHORT_VERSIONS: "5.7,8.0,8.4,9.5"
run: |
export HOME="$GITHUB_WORKSPACE/home"
mkdir -p "$HOME/sandboxes" "$HOME/.dbdeployer"
echo '{}' > "$HOME/.dbdeployer/sandboxes.json"
export PATH="$GITHUB_WORKSPACE:$PATH"
go test ./ts/ -v -count=1 -run TestReplication -timeout 30m
- name: Cleanup
if: always()
run: |
./dbdeployer delete all --skip-confirm 2>/dev/null || true
pkill -9 -u "$USER" mysqld 2>/dev/null || true
# Test PostgreSQL provider: deb extraction, single sandbox, replication,
# cross-database validation.
postgresql-test:
name: PostgreSQL ${{ matrix.pg-version }}
runs-on: ubuntu-22.04
strategy:
fail-fast: false
matrix:
pg-version: ['16']
env:
GO111MODULE: on
PG_VERSION: ${{ matrix.pg-version }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: '1.22'
- name: Add PostgreSQL apt repo
run: |
sudo apt-get update
sudo apt-get install -y curl ca-certificates
sudo install -d /usr/share/postgresql-common/pgdg
sudo curl -o /usr/share/postgresql-common/pgdg/apt.postgresql.org.asc --fail https://www.postgresql.org/media/keys/ACCC4CF8.asc
echo "deb [signed-by=/usr/share/postgresql-common/pgdg/apt.postgresql.org.asc] https://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" | sudo tee /etc/apt/sources.list.d/pgdg.list
sudo apt-get update
- name: Build dbdeployer
run: go build -o dbdeployer .
- name: Install PostgreSQL and set up binaries
run: |
# Install PostgreSQL to get properly configured binaries
sudo apt-get install -y postgresql-${PG_VERSION} postgresql-client-${PG_VERSION}
# Stop the system service — we'll manage our own instances
sudo systemctl stop postgresql || true
# Get full version and copy binaries into dbdeployer's expected layout
PG_FULL=$(dpkg -s postgresql-${PG_VERSION} | grep '^Version:' | sed 's/Version: //' | cut -d'-' -f1)
echo "PostgreSQL version: ${PG_FULL}"
mkdir -p ~/opt/postgresql/${PG_FULL}/{bin,lib,share}
cp -a /usr/lib/postgresql/${PG_VERSION}/bin/. ~/opt/postgresql/${PG_FULL}/bin/
cp -a /usr/lib/postgresql/${PG_VERSION}/lib/. ~/opt/postgresql/${PG_FULL}/lib/
cp -a /usr/share/postgresql/${PG_VERSION}/. ~/opt/postgresql/${PG_FULL}/share/
ls ~/opt/postgresql/${PG_FULL}/bin/
- name: Test deploy postgresql (single)
run: |
PG_FULL=$(ls ~/opt/postgresql/ | head -1)
echo "=== Deploying PostgreSQL $PG_FULL single ==="
./dbdeployer deploy postgresql "$PG_FULL"
echo "=== Connecting ==="
~/sandboxes/pg_sandbox_*/use -c "SELECT version();"
echo "=== Status ==="
~/sandboxes/pg_sandbox_*/status
echo "=== Stop + Start ==="
~/sandboxes/pg_sandbox_*/stop
~/sandboxes/pg_sandbox_*/start
sleep 2
~/sandboxes/pg_sandbox_*/use -c "SELECT 1;"
echo "=== Cleanup ==="
~/sandboxes/pg_sandbox_*/stop
rm -rf ~/sandboxes/pg_sandbox_*
- name: Test deploy replication --provider=postgresql
run: |
PG_FULL=$(ls ~/opt/postgresql/ | head -1)
echo "=== Deploying PostgreSQL $PG_FULL replication ==="
./dbdeployer deploy replication "$PG_FULL" --provider=postgresql --nodes=3
echo "=== Checking topology dir ==="
ls ~/sandboxes/postgresql_repl_*/
echo "=== Check replication ==="
bash ~/sandboxes/postgresql_repl_*/check_replication || true
echo "=== Check recovery ==="
bash ~/sandboxes/postgresql_repl_*/check_recovery || true
echo "=== Primary: write test ==="
~/sandboxes/postgresql_repl_*/primary/use -c "CREATE TABLE test_repl(id serial, val text); INSERT INTO test_repl(val) VALUES ('hello');"
sleep 2
echo "=== Replica 1: read test ==="
~/sandboxes/postgresql_repl_*/replica1/use -c "SELECT * FROM test_repl;"
echo "=== Replica 2: read test ==="
~/sandboxes/postgresql_repl_*/replica2/use -c "SELECT * FROM test_repl;"
- name: Test deploy multiple --provider=postgresql
run: |
PG_FULL=$(ls ~/opt/postgresql/ | head -1)
echo "=== Deploying PostgreSQL $PG_FULL multiple (3 nodes) ==="
./dbdeployer deploy multiple "$PG_FULL" --provider=postgresql --nodes=3
echo "=== Checking topology dir ==="
ls ~/sandboxes/multi_msb_*/
echo "=== Connect to node1 ==="
~/sandboxes/multi_msb_*/node1/use -c "SELECT version();"
~/sandboxes/multi_msb_*/node1/use -c "CREATE TABLE multi_test(id serial, val text); INSERT INTO multi_test(val) VALUES ('from_node1');"
echo "=== Connect to node2 ==="
~/sandboxes/multi_msb_*/node2/use -c "SELECT version();"
~/sandboxes/multi_msb_*/node2/use -c "INSERT INTO multi_test(val) VALUES ('from_node2');" || true
echo "=== Connect to node3 ==="
~/sandboxes/multi_msb_*/node3/use -c "SELECT version();"
echo "=== Cleanup ==="
for dir in ~/sandboxes/multi_msb_*; do
[ -d "$dir" ] && bash "$dir/stop" 2>/dev/null || true
rm -rf "$dir"
done
- name: Test cross-database validation
run: |
PG_FULL=$(ls ~/opt/postgresql/ | head -1)
echo "=== Test: --flavor with --provider=postgresql should fail ==="
if ./dbdeployer deploy single "$PG_FULL" --provider=postgresql --flavor=ndb 2>&1; then
echo "ERROR: should have failed"
exit 1
else
echo "OK: correctly rejected --flavor with --provider=postgresql"
fi
- name: Cleanup
if: always()
run: |
for dir in ~/sandboxes/pg_sandbox_* ~/sandboxes/postgresql_*; do
[ -d "$dir" ] && bash "$dir/stop" 2>/dev/null || true
rm -rf "$dir"
done 2>/dev/null || true
pkill -9 -u "$USER" postgres 2>/dev/null || true
# Test InnoDB Cluster topology with MySQL Shell + MySQL Router
innodb-cluster-test:
name: InnoDB Cluster (${{ matrix.mysql-version }})
runs-on: ubuntu-22.04
strategy:
fail-fast: false
matrix:
mysql-version:
- '8.4.8'
- '9.5.0'
env:
GO111MODULE: on
SANDBOX_BINARY: ${{ github.workspace }}/opt/mysql
MYSQL_VERSION: ${{ matrix.mysql-version }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: '1.22'
- name: Install system libraries
run: |
sudo apt-get update
sudo apt-get install -y libaio1 libnuma1 libncurses5
- name: Build dbdeployer
run: go build -o dbdeployer .
- name: Cache MySQL tarball
uses: actions/cache@v4
with:
path: /tmp/mysql-tarball
key: mysql-${{ matrix.mysql-version }}-linux-x86_64-v2
- name: Download MySQL Server
env:
SHORT_VER_ENV: ${{ matrix.mysql-version }}
run: |
SHORT_VER="${SHORT_VER_ENV%.*}"
TARBALL="mysql-${MYSQL_VERSION}-linux-glibc2.17-x86_64.tar.xz"
mkdir -p /tmp/mysql-tarball
if [ ! -f "/tmp/mysql-tarball/$TARBALL" ]; then
curl -L -f -o "/tmp/mysql-tarball/$TARBALL" \
"https://dev.mysql.com/get/Downloads/MySQL-${SHORT_VER}/$TARBALL"
fi
mkdir -p "$SANDBOX_BINARY"
./dbdeployer unpack "/tmp/mysql-tarball/$TARBALL" --sandbox-binary="$SANDBOX_BINARY"
- name: Download and install MySQL Shell
env:
SHORT_VER_ENV: ${{ matrix.mysql-version }}
run: |
SHORT_VER="${SHORT_VER_ENV%.*}"
SHELL_TARBALL="mysql-shell-${MYSQL_VERSION}-linux-glibc2.17-x86-64bit.tar.gz"
echo "Downloading MySQL Shell ${MYSQL_VERSION}..."
curl -L -f -o "/tmp/$SHELL_TARBALL" \
"https://dev.mysql.com/get/Downloads/MySQL-Shell-${SHORT_VER}/$SHELL_TARBALL"
tar xzf "/tmp/$SHELL_TARBALL" -C /tmp/
SHELL_DIR=$(ls -d /tmp/mysql-shell-${MYSQL_VERSION}* | head -1)
cp "$SHELL_DIR/bin/mysqlsh" "$SANDBOX_BINARY/${MYSQL_VERSION}/bin/"
echo "mysqlsh installed at $SANDBOX_BINARY/${MYSQL_VERSION}/bin/mysqlsh"
- name: Download and install MySQL Router
env:
SHORT_VER_ENV: ${{ matrix.mysql-version }}
run: |
SHORT_VER="${SHORT_VER_ENV%.*}"
ROUTER_TARBALL="mysql-router-${MYSQL_VERSION}-linux-glibc2.17-x86_64.tar.xz"
echo "Downloading MySQL Router ${MYSQL_VERSION}..."
curl -L -f -o "/tmp/$ROUTER_TARBALL" \
"https://dev.mysql.com/get/Downloads/MySQL-Router-${SHORT_VER}/$ROUTER_TARBALL"
tar xJf "/tmp/$ROUTER_TARBALL" -C /tmp/
ROUTER_DIR=$(ls -d /tmp/mysql-router-${MYSQL_VERSION}* | head -1)
cp "$ROUTER_DIR/bin/mysqlrouter" "$SANDBOX_BINARY/${MYSQL_VERSION}/bin/"
cp -r "$ROUTER_DIR/lib/." "$SANDBOX_BINARY/${MYSQL_VERSION}/lib/" 2>/dev/null || true
echo "mysqlrouter installed at $SANDBOX_BINARY/${MYSQL_VERSION}/bin/mysqlrouter"
- name: Install ProxySQL
run: |
PROXYSQL_VERSION="3.0.6"
wget -nv -O /tmp/proxysql.deb \
"https://github.com/sysown/proxysql/releases/download/v${PROXYSQL_VERSION}/proxysql_${PROXYSQL_VERSION}-ubuntu22_amd64.deb"
mkdir -p /tmp/proxysql-extract
dpkg-deb -x /tmp/proxysql.deb /tmp/proxysql-extract
sudo cp /tmp/proxysql-extract/usr/bin/proxysql /usr/local/bin/proxysql
sudo chmod +x /usr/local/bin/proxysql
- name: Test InnoDB Cluster with MySQL Router
run: |
echo "=== Deploy InnoDB Cluster ${MYSQL_VERSION} with Router ==="
./dbdeployer deploy replication "$MYSQL_VERSION" \
--topology=innodb-cluster \
--sandbox-binary="$SANDBOX_BINARY" \
--nodes=3
echo "=== Verify cluster status ==="
~/sandboxes/ic_msb_*/check_cluster
echo "=== Functional test: write on primary, read on all nodes ==="
# Find the primary node (node1)
SBDIR=$(ls -d ~/sandboxes/ic_msb_*)
$SBDIR/node1/use -e "CREATE DATABASE ic_test; USE ic_test; CREATE TABLE t1 (id INT AUTO_INCREMENT PRIMARY KEY, val VARCHAR(100)); INSERT INTO t1 (val) VALUES ('hello_from_primary');"
sleep 3
echo "--- Read from node2 (should see replicated data) ---"
RESULT=$($SBDIR/node2/use -e "SELECT val FROM ic_test.t1;" 2>&1)
echo "$RESULT"
echo "$RESULT" | grep -q "hello_from_primary" || { echo "FAIL: data not replicated to node2"; exit 1; }
echo "--- Read from node3 ---"
RESULT=$($SBDIR/node3/use -e "SELECT val FROM ic_test.t1;" 2>&1)
echo "$RESULT"
echo "$RESULT" | grep -q "hello_from_primary" || { echo "FAIL: data not replicated to node3"; exit 1; }
echo "=== Functional test: connect through MySQL Router (R/W) ==="
ROUTER_RW_PORT=$(ls $SBDIR/router/mysqlrouter.conf 2>/dev/null && grep -A5 '\[routing:bootstrap_rw\]' $SBDIR/router/mysqlrouter.conf | grep 'bind_port' | awk -F= '{print $2}' | tr -d ' ' || echo "")
if [ -n "$ROUTER_RW_PORT" ]; then
echo "Router R/W port: $ROUTER_RW_PORT"
$SBDIR/node1/use -h 127.0.0.1 -P "$ROUTER_RW_PORT" -e "INSERT INTO ic_test.t1 (val) VALUES ('via_router');"
sleep 2
RESULT=$($SBDIR/node2/use -e "SELECT val FROM ic_test.t1 WHERE val='via_router';" 2>&1)
echo "$RESULT"
echo "$RESULT" | grep -q "via_router" || { echo "FAIL: write through Router not replicated"; exit 1; }
echo "OK: Router R/W connection works and replication verified"
else
echo "WARN: Could not determine Router R/W port, skipping Router R/W connection test"
fi
echo "=== Functional test: connect through MySQL Router (R/O) ==="
ROUTER_RO_PORT=$(ls $SBDIR/router/mysqlrouter.conf 2>/dev/null && grep -A5 '\[routing:bootstrap_ro\]' $SBDIR/router/mysqlrouter.conf | grep 'bind_port' | awk -F= '{print $2}' | tr -d ' ' || echo "")
if [ -n "$ROUTER_RO_PORT" ]; then
echo "Router R/O port: $ROUTER_RO_PORT"
RESULT=$($SBDIR/node1/use -h 127.0.0.1 -P "$ROUTER_RO_PORT" -e "SELECT val FROM ic_test.t1 WHERE val='hello_from_primary';" 2>&1)
echo "$RESULT"
echo "$RESULT" | grep -q "hello_from_primary" || { echo "FAIL: SELECT through Router R/O port failed"; exit 1; }
echo "OK: Router R/O SELECT succeeded"
else
echo "WARN: Could not determine Router R/O port, skipping Router R/O connection test"
fi
echo "=== Cleanup ==="
./dbdeployer delete all --skip-confirm
- name: Test InnoDB Cluster with --skip-router + write/read verification
run: |
echo "=== Deploy InnoDB Cluster ${MYSQL_VERSION} without Router ==="
./dbdeployer deploy replication "$MYSQL_VERSION" \
--topology=innodb-cluster \
--skip-router \
--sandbox-binary="$SANDBOX_BINARY" \
--nodes=3
echo "=== Verify cluster status ==="
~/sandboxes/ic_msb_*/check_cluster
echo "=== Functional test: write/read across cluster ==="
SBDIR=$(ls -d ~/sandboxes/ic_msb_*)
$SBDIR/node1/use -e "CREATE DATABASE skiprt_test; USE skiprt_test; CREATE TABLE t1 (id INT AUTO_INCREMENT PRIMARY KEY, msg TEXT); INSERT INTO t1 (msg) VALUES ('skip_router_test');"
sleep 3
RESULT=$($SBDIR/node3/use -e "SELECT msg FROM skiprt_test.t1;" 2>&1)
echo "$RESULT"
echo "$RESULT" | grep -q "skip_router_test" || { echo "FAIL: data not replicated"; exit 1; }
echo "OK: InnoDB Cluster replication works without Router"
echo "=== Cleanup ==="
./dbdeployer delete all --skip-confirm
- name: Test InnoDB Cluster with ProxySQL (instead of Router)
run: |
echo "=== Deploy InnoDB Cluster ${MYSQL_VERSION} + ProxySQL ==="
./dbdeployer deploy replication "$MYSQL_VERSION" \
--topology=innodb-cluster \
--skip-router \
--with-proxysql \
--sandbox-binary="$SANDBOX_BINARY" \
--nodes=3
echo "=== Verify cluster status ==="
~/sandboxes/ic_msb_*/check_cluster
echo "=== Verify ProxySQL sees the backend servers ==="
SBDIR=$(ls -d ~/sandboxes/ic_msb_*)
SERVERS=$($SBDIR/proxysql/use -e "SELECT hostname, port, hostgroup_id, status FROM runtime_mysql_servers;" 2>&1)
echo "$SERVERS"
# Verify at least 2 servers are ONLINE
ONLINE_COUNT=$(echo "$SERVERS" | grep -c "ONLINE" || true)
echo "Online servers: $ONLINE_COUNT"
[ "$ONLINE_COUNT" -ge 2 ] || { echo "FAIL: expected at least 2 ONLINE servers in ProxySQL"; exit 1; }
echo "=== Functional test: write through ProxySQL ==="
$SBDIR/proxysql/use_proxy -e "CREATE DATABASE proxy_ic_test; USE proxy_ic_test; CREATE TABLE t1 (id INT AUTO_INCREMENT PRIMARY KEY, val VARCHAR(100)); INSERT INTO t1 (val) VALUES ('via_proxysql');"
sleep 3
echo "--- Verify on node2 directly ---"
RESULT=$($SBDIR/node2/use -e "SELECT val FROM proxy_ic_test.t1;" 2>&1)
echo "$RESULT"
echo "$RESULT" | grep -q "via_proxysql" || { echo "FAIL: write through ProxySQL not replicated"; exit 1; }
echo "OK: ProxySQL -> InnoDB Cluster write + replication verified"
- name: Cleanup
if: always()
run: |
./dbdeployer delete all --skip-confirm 2>/dev/null || true
pkill -9 -u "$USER" mysqld 2>/dev/null || true
pkill -9 -u "$USER" mysqlrouter 2>/dev/null || true
pkill -9 -u "$USER" proxysql 2>/dev/null || true
# Test the "downloads get-by-version" + "unpack" flow that users follow
# from the quickstart guide. This catches registry gaps and download issues.
downloads-test:
name: Downloads get-by-version (${{ matrix.short-version }})
runs-on: ubuntu-22.04
strategy:
fail-fast: false
matrix:
short-version:
- '8.4'
- '9.5'
env:
GO111MODULE: on
SANDBOX_BINARY: ${{ github.workspace }}/opt/mysql
SHORT_VERSION: ${{ matrix.short-version }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: '1.22'
- name: Install system libraries
run: |
sudo apt-get update
sudo apt-get install -y libaio1 libnuma1 libncurses5
- name: Build dbdeployer
run: go build -o dbdeployer .
- name: Test downloads get-by-version
run: |
echo "=== Testing: dbdeployer downloads get-by-version $SHORT_VERSION ==="
./dbdeployer downloads get-by-version "$SHORT_VERSION" --newest --minimal
echo "=== Download complete ==="
ls -lh mysql-*.tar.*
- name: Test unpack
run: |
TARBALL=$(ls mysql-*.tar.* | head -1)
echo "=== Unpacking: $TARBALL ==="
mkdir -p "$SANDBOX_BINARY"
./dbdeployer unpack "$TARBALL" --sandbox-binary="$SANDBOX_BINARY"
echo "=== Available versions ==="
./dbdeployer versions --sandbox-binary="$SANDBOX_BINARY"
- name: Test deploy from downloaded binary
run: |
VERSION=$(./dbdeployer versions --sandbox-binary="$SANDBOX_BINARY" | grep -oP '\d+\.\d+\.\d+' | head -1)
echo "=== Deploying: $VERSION ==="
./dbdeployer deploy single "$VERSION" --sandbox-binary="$SANDBOX_BINARY"
~/sandboxes/msb_*/use -e "SELECT VERSION()"
./dbdeployer delete all --skip-confirm
- name: Cleanup
if: always()
run: |
./dbdeployer delete all --skip-confirm 2>/dev/null || true
pkill -9 -u "$USER" mysqld 2>/dev/null || true
# Test group replication topologies: single-primary and multi-primary (#64)
group-replication-test:
name: Group Replication (${{ matrix.mysql-version }})
runs-on: ubuntu-22.04
strategy:
fail-fast: false
matrix:
mysql-version:
- '8.4.8'
env:
GO111MODULE: on
SANDBOX_BINARY: ${{ github.workspace }}/opt/mysql
MYSQL_VERSION: ${{ matrix.mysql-version }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: '1.22'
- name: Install system libraries
run: |
sudo apt-get update
sudo apt-get install -y libaio1 libnuma1 libncurses5
- name: Build dbdeployer
run: go build -o dbdeployer .
- name: Cache MySQL tarball
uses: actions/cache@v4
with:
path: /tmp/mysql-tarball
key: mysql-${{ matrix.mysql-version }}-linux-x86_64-v1
- name: Download and unpack MySQL
env:
SHORT_VER_ENV: ${{ matrix.mysql-version }}
run: |
SHORT_VER="${SHORT_VER_ENV%.*}"
TARBALL="mysql-${MYSQL_VERSION}-linux-glibc2.17-x86_64.tar.xz"
mkdir -p /tmp/mysql-tarball
if [ ! -f "/tmp/mysql-tarball/$TARBALL" ]; then
curl -L -f -o "/tmp/mysql-tarball/$TARBALL" \
"https://dev.mysql.com/get/Downloads/MySQL-${SHORT_VER}/$TARBALL"
fi
mkdir -p "$SANDBOX_BINARY"
./dbdeployer unpack "/tmp/mysql-tarball/$TARBALL" --sandbox-binary="$SANDBOX_BINARY"
- name: Test group replication (single-primary)
run: |
echo "=== Deploy group replication single-primary ${MYSQL_VERSION} ==="
./dbdeployer deploy replication "$MYSQL_VERSION" \
--topology=group \
--sandbox-binary="$SANDBOX_BINARY" \
--nodes=3
SBDIR=$(ls -d ~/sandboxes/group_msb_*)
echo "=== Verify all nodes ONLINE ==="
ONLINE_COUNT=$($SBDIR/node1/use -BN -e "SELECT COUNT(*) FROM performance_schema.replication_group_members WHERE MEMBER_STATE='ONLINE';" 2>&1)
echo "Online members: $ONLINE_COUNT"
[ "$ONLINE_COUNT" -eq 3 ] || { echo "FAIL: expected 3 ONLINE members, got $ONLINE_COUNT"; exit 1; }
echo "=== Identify primary ==="
PRIMARY_PORT=$($SBDIR/node1/use -BN -e "SELECT @@port;" 2>&1)
IS_PRIMARY=$($SBDIR/node1/use -BN -e "SELECT COUNT(*) FROM performance_schema.replication_group_members WHERE MEMBER_ROLE='PRIMARY' AND MEMBER_PORT=$PRIMARY_PORT;" 2>&1)
echo "node1 is primary: $IS_PRIMARY"
echo "=== Write on primary (node1), read on secondary (node2) ==="
$SBDIR/node1/use -e "CREATE DATABASE gr_test; USE gr_test; CREATE TABLE t1 (id INT AUTO_INCREMENT PRIMARY KEY, val VARCHAR(100)); INSERT INTO t1 (val) VALUES ('single_primary_write');"
sleep 3
RESULT=$($SBDIR/node2/use -BN -e "SELECT val FROM gr_test.t1;" 2>&1)
echo "node2 result: $RESULT"
echo "$RESULT" | grep -q "single_primary_write" || { echo "FAIL: data not replicated to node2"; exit 1; }
RESULT=$($SBDIR/node3/use -BN -e "SELECT val FROM gr_test.t1;" 2>&1)
echo "node3 result: $RESULT"
echo "$RESULT" | grep -q "single_primary_write" || { echo "FAIL: data not replicated to node3"; exit 1; }
echo "OK: group replication single-primary write+read verified"
echo "=== Cleanup ==="
./dbdeployer delete all --skip-confirm
- name: Test group replication (multi-primary)
run: |
echo "=== Deploy group replication multi-primary ${MYSQL_VERSION} ==="
./dbdeployer deploy replication "$MYSQL_VERSION" \
--topology=group \
--single-primary=false \
--sandbox-binary="$SANDBOX_BINARY" \
--nodes=3
SBDIR=$(ls -d ~/sandboxes/group_msb_*)
echo "=== Verify all nodes ONLINE ==="
ONLINE_COUNT=$($SBDIR/node1/use -BN -e "SELECT COUNT(*) FROM performance_schema.replication_group_members WHERE MEMBER_STATE='ONLINE';" 2>&1)
echo "Online members: $ONLINE_COUNT"
[ "$ONLINE_COUNT" -eq 3 ] || { echo "FAIL: expected 3 ONLINE members, got $ONLINE_COUNT"; exit 1; }
echo "=== Verify multi-primary: all nodes are PRIMARY ==="
PRIMARY_COUNT=$($SBDIR/node1/use -BN -e "SELECT COUNT(*) FROM performance_schema.replication_group_members WHERE MEMBER_ROLE='PRIMARY';" 2>&1)
echo "Primary count: $PRIMARY_COUNT"
[ "$PRIMARY_COUNT" -eq 3 ] || { echo "FAIL: expected 3 PRIMARY members in multi-primary mode, got $PRIMARY_COUNT"; exit 1; }
echo "=== Write on node1, read on node3 ==="
$SBDIR/node1/use -e "CREATE DATABASE gr_mp_test; USE gr_mp_test; CREATE TABLE t1 (id INT AUTO_INCREMENT PRIMARY KEY, val VARCHAR(100)); INSERT INTO t1 (val) VALUES ('write_from_node1');"
sleep 3
RESULT=$($SBDIR/node3/use -BN -e "SELECT val FROM gr_mp_test.t1 WHERE val='write_from_node1';" 2>&1)
echo "node3 sees node1 write: $RESULT"
echo "$RESULT" | grep -q "write_from_node1" || { echo "FAIL: node1 write not replicated to node3"; exit 1; }
echo "=== Write on node3, read on node1 ==="
$SBDIR/node3/use -e "INSERT INTO gr_mp_test.t1 (val) VALUES ('write_from_node3');"
sleep 3
RESULT=$($SBDIR/node1/use -BN -e "SELECT val FROM gr_mp_test.t1 WHERE val='write_from_node3';" 2>&1)
echo "node1 sees node3 write: $RESULT"
echo "$RESULT" | grep -q "write_from_node3" || { echo "FAIL: node3 write not replicated to node1"; exit 1; }
echo "OK: group replication multi-primary write+read verified"
echo "=== Cleanup ==="
./dbdeployer delete all --skip-confirm
- name: Cleanup
if: always()
run: |
./dbdeployer delete all --skip-confirm 2>/dev/null || true
pkill -9 -u "$USER" mysqld 2>/dev/null || true
# Test fan-in and all-masters replication topologies (#67)
fan-in-all-masters-test:
name: Fan-in and All-masters (${{ matrix.mysql-version }})
runs-on: ubuntu-22.04
strategy:
fail-fast: false
matrix:
mysql-version:
- '8.4.8'
env:
GO111MODULE: on
SANDBOX_BINARY: ${{ github.workspace }}/opt/mysql
MYSQL_VERSION: ${{ matrix.mysql-version }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: '1.22'
- name: Install system libraries
run: |
sudo apt-get update
sudo apt-get install -y libaio1 libnuma1 libncurses5
- name: Build dbdeployer
run: go build -o dbdeployer .
- name: Cache MySQL tarball
uses: actions/cache@v4
with:
path: /tmp/mysql-tarball
key: mysql-${{ matrix.mysql-version }}-linux-x86_64-v1
- name: Download and unpack MySQL
env:
SHORT_VER_ENV: ${{ matrix.mysql-version }}
run: |
SHORT_VER="${SHORT_VER_ENV%.*}"
TARBALL="mysql-${MYSQL_VERSION}-linux-glibc2.17-x86_64.tar.xz"
mkdir -p /tmp/mysql-tarball
if [ ! -f "/tmp/mysql-tarball/$TARBALL" ]; then
curl -L -f -o "/tmp/mysql-tarball/$TARBALL" \
"https://dev.mysql.com/get/Downloads/MySQL-${SHORT_VER}/$TARBALL"
fi
mkdir -p "$SANDBOX_BINARY"
./dbdeployer unpack "/tmp/mysql-tarball/$TARBALL" --sandbox-binary="$SANDBOX_BINARY"
- name: Test fan-in topology
run: |
echo "=== Deploy fan-in ${MYSQL_VERSION} (3 nodes) ==="
# Fan-in: multiple masters feed into one slave (the last node)
./dbdeployer deploy replication "$MYSQL_VERSION" \
--topology=fan-in \
--sandbox-binary="$SANDBOX_BINARY" \
--nodes=3
SBDIR=$(ls -d ~/sandboxes/fan_in_msb_*)
echo "=== Verify replication ==="
bash "$SBDIR/check_slaves" || bash "$SBDIR/check_replication" || true
echo "=== Write on master1 (node1), read on common node (node3) ==="
$SBDIR/node1/use -e "CREATE DATABASE fanin_test; USE fanin_test; CREATE TABLE t1 (id INT AUTO_INCREMENT PRIMARY KEY, val VARCHAR(100), src VARCHAR(20)); INSERT INTO t1 (val, src) VALUES ('from_master1', 'node1');"
sleep 3
RESULT=$($SBDIR/node3/use -BN -e "SELECT val FROM fanin_test.t1 WHERE src='node1';" 2>&1)
echo "node3 sees node1 write: $RESULT"
echo "$RESULT" | grep -q "from_master1" || { echo "FAIL: node1 write not replicated to node3"; exit 1; }
echo "=== Write on master2 (node2), read on common node (node3) ==="
$SBDIR/node2/use -e "INSERT INTO fanin_test.t1 (val, src) VALUES ('from_master2', 'node2');"
sleep 3
RESULT=$($SBDIR/node3/use -BN -e "SELECT val FROM fanin_test.t1 WHERE src='node2';" 2>&1)
echo "node3 sees node2 write: $RESULT"
echo "$RESULT" | grep -q "from_master2" || { echo "FAIL: node2 write not replicated to node3"; exit 1; }
echo "OK: fan-in topology write+read verified"
echo "=== Cleanup ==="
./dbdeployer delete all --skip-confirm
- name: Test all-masters topology
run: |
echo "=== Deploy all-masters ${MYSQL_VERSION} (3 nodes) ==="
./dbdeployer deploy replication "$MYSQL_VERSION" \
--topology=all-masters \
--sandbox-binary="$SANDBOX_BINARY" \
--nodes=3
SBDIR=$(ls -d ~/sandboxes/all_masters_msb_*)
echo "=== Verify replication ==="
bash "$SBDIR/check_slaves" || bash "$SBDIR/check_replication" || true
echo "=== Write on node1, read on node3 ==="
$SBDIR/node1/use -e "CREATE DATABASE allm_test; USE allm_test; CREATE TABLE t1 (id INT AUTO_INCREMENT PRIMARY KEY, val VARCHAR(100)); INSERT INTO t1 (val) VALUES ('write_from_node1');"
sleep 3
RESULT=$($SBDIR/node3/use -BN -e "SELECT val FROM allm_test.t1 WHERE val='write_from_node1';" 2>&1)
echo "node3 sees node1 write: $RESULT"
echo "$RESULT" | grep -q "write_from_node1" || { echo "FAIL: node1 write not replicated to node3"; exit 1; }
echo "=== Write on node3, read on node1 ==="
$SBDIR/node3/use -e "INSERT INTO allm_test.t1 (val) VALUES ('write_from_node3');"
sleep 3
RESULT=$($SBDIR/node1/use -BN -e "SELECT val FROM allm_test.t1 WHERE val='write_from_node3';" 2>&1)
echo "node1 sees node3 write: $RESULT"
echo "$RESULT" | grep -q "write_from_node3" || { echo "FAIL: node3 write not replicated to node1"; exit 1; }
echo "OK: all-masters topology write+read verified"
echo "=== Cleanup ==="
./dbdeployer delete all --skip-confirm
- name: Cleanup
if: always()
run: |
./dbdeployer delete all --skip-confirm 2>/dev/null || true
pkill -9 -u "$USER" mysqld 2>/dev/null || true