ci: add ts replication test suite (5.7, 8.0, 8.4, 9.5) #143
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 |