From a1dfaa6ac3dbc17e3377a81c4d69b6723bc6e763 Mon Sep 17 00:00:00 2001 From: Renan Rangel Date: Tue, 24 Sep 2024 18:31:29 +0100 Subject: [PATCH 1/8] adding new mysql shell backup engine (#16295) Signed-off-by: Renan Rangel Signed-off-by: Shlomi Noach <2607934+shlomi-noach@users.noreply.github.com> Co-authored-by: Shlomi Noach <2607934+shlomi-noach@users.noreply.github.com> --- .github/workflows/cluster_endtoend_12.yml | 2 +- .github/workflows/cluster_endtoend_13.yml | 2 +- .github/workflows/cluster_endtoend_15.yml | 2 +- .github/workflows/cluster_endtoend_18.yml | 2 +- .github/workflows/cluster_endtoend_21.yml | 2 +- .github/workflows/cluster_endtoend_22.yml | 2 +- .../cluster_endtoend_backup_pitr.yml | 2 +- ...luster_endtoend_backup_pitr_mysqlshell.yml | 154 +++++ ...ter_endtoend_ers_prs_newfeatures_heavy.yml | 2 +- .../workflows/cluster_endtoend_mysql80.yml | 2 +- .../cluster_endtoend_mysql_server_vault.yml | 2 +- .../cluster_endtoend_onlineddl_ghost.yml | 2 +- .../cluster_endtoend_onlineddl_revert.yml | 2 +- .../cluster_endtoend_onlineddl_scheduler.yml | 2 +- .../cluster_endtoend_onlineddl_vrepl.yml | 2 +- ...luster_endtoend_onlineddl_vrepl_stress.yml | 2 +- ..._endtoend_onlineddl_vrepl_stress_suite.yml | 2 +- ...cluster_endtoend_onlineddl_vrepl_suite.yml | 2 +- .../cluster_endtoend_schemadiff_vrepl.yml | 2 +- .../cluster_endtoend_tabletmanager_consul.yml | 2 +- ...cluster_endtoend_tabletmanager_tablegc.yml | 2 +- ..._endtoend_tabletmanager_throttler_topo.yml | 2 +- ...cluster_endtoend_topo_connection_cache.yml | 2 +- ...dtoend_vreplication_across_db_versions.yml | 2 +- .../cluster_endtoend_vreplication_basic.yml | 2 +- ...luster_endtoend_vreplication_cellalias.yml | 2 +- ...dtoend_vreplication_foreign_key_stress.yml | 2 +- ...endtoend_vreplication_mariadb_to_mysql.yml | 179 ++++++ ...vreplication_migrate_vdiff2_convert_tz.yml | 2 +- ...ion_partial_movetables_and_materialize.yml | 2 +- .../cluster_endtoend_vreplication_v2.yml | 2 +- .../workflows/cluster_endtoend_vstream.yml | 2 +- .../workflows/cluster_endtoend_vtbackup.yml | 2 +- ..._vtctlbackup_sharded_clustertest_heavy.yml | 2 +- .../cluster_endtoend_vtgate_concurrentdml.yml | 2 +- ...ster_endtoend_vtgate_foreignkey_stress.yml | 2 +- .../cluster_endtoend_vtgate_gen4.yml | 2 +- .../cluster_endtoend_vtgate_general_heavy.yml | 2 +- .../cluster_endtoend_vtgate_godriver.yml | 2 +- ...uster_endtoend_vtgate_partial_keyspace.yml | 2 +- .../cluster_endtoend_vtgate_queries.yml | 2 +- ...cluster_endtoend_vtgate_readafterwrite.yml | 2 +- .../cluster_endtoend_vtgate_reservedconn.yml | 2 +- .../cluster_endtoend_vtgate_schema.yml | 2 +- ...cluster_endtoend_vtgate_schema_tracker.yml | 2 +- ...dtoend_vtgate_tablet_healthcheck_cache.yml | 2 +- .../cluster_endtoend_vtgate_topo.yml | 2 +- .../cluster_endtoend_vtgate_topo_consul.yml | 2 +- .../cluster_endtoend_vtgate_topo_etcd.yml | 2 +- .../cluster_endtoend_vtgate_transaction.yml | 2 +- .../cluster_endtoend_vtgate_unsharded.yml | 2 +- .../cluster_endtoend_vtgate_vindex_heavy.yml | 2 +- .../cluster_endtoend_vtgate_vschema.yml | 2 +- .github/workflows/cluster_endtoend_vtorc.yml | 2 +- .../cluster_endtoend_vttablet_prscomplex.yml | 2 +- changelog/21.0/21.0.0/summary.md | 174 ++++++ config/init_db.sql | 1 + ...ng new mysql shell backup engine (#16295)) | 63 ++ docker/lite/install_dependencies.sh | 2 + examples/compose/config/init_db.sql | 1 + examples/operator/101_initial_cluster.yaml | 1 + examples/operator/operator.yaml | 1 + go/flags/endtoend/vtbackup.txt | 6 + go/flags/endtoend/vtcombo.txt | 6 + go/flags/endtoend/vttablet.txt | 6 + go/flags/endtoend/vttestserver.txt | 6 + .../backup_pitr_mysqlshell_test.go | 61 ++ .../backup/vtctlbackup/backup_utils.go | 92 ++- .../testdata/config/init_testserver_db.sql | 1 + go/vt/mysqlctl/backup.go | 44 +- go/vt/mysqlctl/backup_test.go | 73 +++ go/vt/mysqlctl/backupengine.go | 1 + go/vt/mysqlctl/builtinbackupengine.go | 5 + go/vt/mysqlctl/fakebackupengine.go | 4 + go/vt/mysqlctl/fakemysqldaemon.go | 16 + go/vt/mysqlctl/mysql_daemon.go | 10 + go/vt/mysqlctl/mysqld.go | 7 +- go/vt/mysqlctl/mysqlshellbackupengine.go | 582 ++++++++++++++++++ go/vt/mysqlctl/mysqlshellbackupengine_test.go | 309 ++++++++++ go/vt/mysqlctl/query.go | 37 ++ go/vt/mysqlctl/xtrabackupengine.go | 22 +- test/ci_workflow_gen.go | 1 + test/config.json | 9 + test/templates/cluster_endtoend_test.tpl | 2 +- 84 files changed, 1864 insertions(+), 118 deletions(-) create mode 100644 .github/workflows/cluster_endtoend_backup_pitr_mysqlshell.yml create mode 100644 .github/workflows/cluster_endtoend_vreplication_mariadb_to_mysql.yml create mode 100644 changelog/21.0/21.0.0/summary.md create mode 100644 docker/lite/Dockerfile~969e018d8d (adding new mysql shell backup engine (#16295)) create mode 100644 go/test/endtoend/backup/pitr_mysqlshell/backup_pitr_mysqlshell_test.go create mode 100644 go/vt/mysqlctl/mysqlshellbackupengine.go create mode 100644 go/vt/mysqlctl/mysqlshellbackupengine_test.go diff --git a/.github/workflows/cluster_endtoend_12.yml b/.github/workflows/cluster_endtoend_12.yml index 459e59e9458..e20ce9abae1 100644 --- a/.github/workflows/cluster_endtoend_12.yml +++ b/.github/workflows/cluster_endtoend_12.yml @@ -105,7 +105,7 @@ jobs: sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update # Install everything else we need, and configure - sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 + sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 sudo service mysql stop sudo service etcd stop diff --git a/.github/workflows/cluster_endtoend_13.yml b/.github/workflows/cluster_endtoend_13.yml index 2cd47be95dc..d5a8b73286b 100644 --- a/.github/workflows/cluster_endtoend_13.yml +++ b/.github/workflows/cluster_endtoend_13.yml @@ -105,7 +105,7 @@ jobs: sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update # Install everything else we need, and configure - sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 + sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 sudo service mysql stop sudo service etcd stop diff --git a/.github/workflows/cluster_endtoend_15.yml b/.github/workflows/cluster_endtoend_15.yml index 1e9fe44a18a..002aaad4f13 100644 --- a/.github/workflows/cluster_endtoend_15.yml +++ b/.github/workflows/cluster_endtoend_15.yml @@ -105,7 +105,7 @@ jobs: sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update # Install everything else we need, and configure - sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 + sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 sudo service mysql stop sudo service etcd stop diff --git a/.github/workflows/cluster_endtoend_18.yml b/.github/workflows/cluster_endtoend_18.yml index 465abb6c3e3..5cb72fc679c 100644 --- a/.github/workflows/cluster_endtoend_18.yml +++ b/.github/workflows/cluster_endtoend_18.yml @@ -105,7 +105,7 @@ jobs: sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update # Install everything else we need, and configure - sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 + sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 sudo service mysql stop sudo service etcd stop diff --git a/.github/workflows/cluster_endtoend_21.yml b/.github/workflows/cluster_endtoend_21.yml index 375f8e9ab31..1eed6bdc04a 100644 --- a/.github/workflows/cluster_endtoend_21.yml +++ b/.github/workflows/cluster_endtoend_21.yml @@ -105,7 +105,7 @@ jobs: sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update # Install everything else we need, and configure - sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 + sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 sudo service mysql stop sudo service etcd stop diff --git a/.github/workflows/cluster_endtoend_22.yml b/.github/workflows/cluster_endtoend_22.yml index 4c67161b5a1..0f450b229e1 100644 --- a/.github/workflows/cluster_endtoend_22.yml +++ b/.github/workflows/cluster_endtoend_22.yml @@ -105,7 +105,7 @@ jobs: sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update # Install everything else we need, and configure - sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 + sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 sudo service mysql stop sudo service etcd stop diff --git a/.github/workflows/cluster_endtoend_backup_pitr.yml b/.github/workflows/cluster_endtoend_backup_pitr.yml index 1dcf3a3f656..38f46f46010 100644 --- a/.github/workflows/cluster_endtoend_backup_pitr.yml +++ b/.github/workflows/cluster_endtoend_backup_pitr.yml @@ -105,7 +105,7 @@ jobs: sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update # Install everything else we need, and configure - sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 + sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 sudo service mysql stop sudo service etcd stop diff --git a/.github/workflows/cluster_endtoend_backup_pitr_mysqlshell.yml b/.github/workflows/cluster_endtoend_backup_pitr_mysqlshell.yml new file mode 100644 index 00000000000..ffcf6cdb4c0 --- /dev/null +++ b/.github/workflows/cluster_endtoend_backup_pitr_mysqlshell.yml @@ -0,0 +1,154 @@ +# DO NOT MODIFY: THIS FILE IS GENERATED USING "make generate_ci_workflows" + +name: Cluster (backup_pitr_mysqlshell) +on: [push, pull_request] +concurrency: + group: format('{0}-{1}', ${{ github.ref }}, 'Cluster (backup_pitr_mysqlshell)') + cancel-in-progress: true + +permissions: read-all + +env: + LAUNCHABLE_ORGANIZATION: "vitess" + LAUNCHABLE_WORKSPACE: "vitess-app" + GITHUB_PR_HEAD_SHA: "${{ github.event.pull_request.head.sha }}" + GOPRIVATE: github.com/slackhq/vitess-addons + GH_ACCESS_TOKEN: "${{ secrets.GH_ACCESS_TOKEN }}" + +jobs: + build: + name: Run endtoend tests on Cluster (backup_pitr_mysqlshell) + runs-on: + group: vitess-ubuntu20 + + steps: + - name: Skip CI + run: | + if [[ "${{contains( github.event.pull_request.labels.*.name, 'Skip CI')}}" == "true" ]]; then + echo "skipping CI due to the 'Skip CI' label" + exit 1 + fi + + - name: Check if workflow needs to be skipped + id: skip-workflow + run: | + skip='false' + echo Skip ${skip} + echo "skip-workflow=${skip}" >> $GITHUB_OUTPUT + + PR_DATA=$(curl \ + -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \ + -H "Accept: application/vnd.github.v3+json" \ + "https://api.github.com/repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}") + draft=$(echo "$PR_DATA" | jq .draft -r) + echo "is_draft=${draft}" >> $GITHUB_OUTPUT + + - name: Check out code + if: steps.skip-workflow.outputs.skip-workflow == 'false' + uses: actions/checkout@v4 + + - name: Check for changes in relevant files + if: steps.skip-workflow.outputs.skip-workflow == 'false' + uses: dorny/paths-filter@v3.0.1 + id: changes + with: + token: '' + filters: | + end_to_end: + - 'go/**/*.go' + - 'go/vt/sidecardb/**/*.sql' + - 'go/test/endtoend/onlineddl/vrepl_suite/**' + - 'test.go' + - 'Makefile' + - 'build.env' + - 'go.sum' + - 'go.mod' + - 'proto/*.proto' + - 'tools/**' + - 'config/**' + - 'bootstrap.sh' + - '.github/workflows/cluster_endtoend_backup_pitr_mysqlshell.yml' + + - name: Set up Go + if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' + uses: actions/setup-go@v5 + with: + go-version: 1.22.5 + + - name: Setup github.com/slackhq/vitess-addons access token + if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' + run: git config --global url.https://$GH_ACCESS_TOKEN@github.com/.insteadOf https://github.com/ + + - name: Set up python + if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' + uses: actions/setup-python@v5 + + - name: Tune the OS + if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' + run: | + # Limit local port range to not use ports that overlap with server side + # ports that we listen on. + sudo sysctl -w net.ipv4.ip_local_port_range="22768 65535" + # Increase the asynchronous non-blocking I/O. More information at https://dev.mysql.com/doc/refman/5.7/en/innodb-parameters.html#sysvar_innodb_use_native_aio + echo "fs.aio-max-nr = 1048576" | sudo tee -a /etc/sysctl.conf + sudo sysctl -p /etc/sysctl.conf + + - name: Get dependencies + if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' + run: | + + # Get key to latest MySQL repo + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C + # Setup MySQL 8.0 + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb + echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | sudo debconf-set-selections + sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* + sudo apt-get update + # Install everything else we need, and configure + sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 + + sudo service mysql stop + sudo service etcd stop + sudo ln -s /etc/apparmor.d/usr.sbin.mysqld /etc/apparmor.d/disable/ + sudo apparmor_parser -R /etc/apparmor.d/usr.sbin.mysqld + go mod download + + # install JUnit report formatter + go install github.com/vitessio/go-junit-report@HEAD + + - name: Setup launchable dependencies + if: steps.skip-workflow.outputs.is_draft == 'false' && steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' && github.base_ref == 'main' + run: | + # Get Launchable CLI installed. If you can, make it a part of the builder image to speed things up + pip3 install --user launchable~=1.0 > /dev/null + + # verify that launchable setup is all correct. + launchable verify || true + + # Tell Launchable about the build you are producing and testing + launchable record build --name "$GITHUB_RUN_ID" --no-commit-collection --source . + + - name: Run cluster endtoend test + if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' + timeout-minutes: 45 + run: | + # We set the VTDATAROOT to the /tmp folder to reduce the file path of mysql.sock file + # which musn't be more than 107 characters long. + export VTDATAROOT="/tmp/" + source build.env + + set -exo pipefail + + # run the tests however you normally do, then produce a JUnit XML file + eatmydata -- go run test.go -docker=false -follow -shard backup_pitr_mysqlshell | tee -a output.txt | go-junit-report -set-exit-code > report.xml + + - name: Print test output and Record test result in launchable if PR is not a draft + if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' && always() + run: | + if [[ "${{steps.skip-workflow.outputs.is_draft}}" == "false" ]]; then + # send recorded tests to launchable + launchable record tests --build "$GITHUB_RUN_ID" go-test . || true + fi + + # print test output + cat output.txt diff --git a/.github/workflows/cluster_endtoend_ers_prs_newfeatures_heavy.yml b/.github/workflows/cluster_endtoend_ers_prs_newfeatures_heavy.yml index a5ce553b5cd..b1b54c2b104 100644 --- a/.github/workflows/cluster_endtoend_ers_prs_newfeatures_heavy.yml +++ b/.github/workflows/cluster_endtoend_ers_prs_newfeatures_heavy.yml @@ -105,7 +105,7 @@ jobs: sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update # Install everything else we need, and configure - sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 + sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 sudo service mysql stop sudo service etcd stop diff --git a/.github/workflows/cluster_endtoend_mysql80.yml b/.github/workflows/cluster_endtoend_mysql80.yml index 1cec0679b9d..e48ec212696 100644 --- a/.github/workflows/cluster_endtoend_mysql80.yml +++ b/.github/workflows/cluster_endtoend_mysql80.yml @@ -105,7 +105,7 @@ jobs: sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update # Install everything else we need, and configure - sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 + sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 sudo service mysql stop sudo service etcd stop diff --git a/.github/workflows/cluster_endtoend_mysql_server_vault.yml b/.github/workflows/cluster_endtoend_mysql_server_vault.yml index 2388d9b6648..ee447b53385 100644 --- a/.github/workflows/cluster_endtoend_mysql_server_vault.yml +++ b/.github/workflows/cluster_endtoend_mysql_server_vault.yml @@ -105,7 +105,7 @@ jobs: sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update # Install everything else we need, and configure - sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 + sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 sudo service mysql stop sudo service etcd stop diff --git a/.github/workflows/cluster_endtoend_onlineddl_ghost.yml b/.github/workflows/cluster_endtoend_onlineddl_ghost.yml index 27ae764995f..1494f1d7420 100644 --- a/.github/workflows/cluster_endtoend_onlineddl_ghost.yml +++ b/.github/workflows/cluster_endtoend_onlineddl_ghost.yml @@ -106,7 +106,7 @@ jobs: sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update # Install everything else we need, and configure - sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 + sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 sudo service mysql stop sudo service etcd stop diff --git a/.github/workflows/cluster_endtoend_onlineddl_revert.yml b/.github/workflows/cluster_endtoend_onlineddl_revert.yml index 4b542988c2e..8ff589e747a 100644 --- a/.github/workflows/cluster_endtoend_onlineddl_revert.yml +++ b/.github/workflows/cluster_endtoend_onlineddl_revert.yml @@ -106,7 +106,7 @@ jobs: sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update # Install everything else we need, and configure - sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 + sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 sudo service mysql stop sudo service etcd stop diff --git a/.github/workflows/cluster_endtoend_onlineddl_scheduler.yml b/.github/workflows/cluster_endtoend_onlineddl_scheduler.yml index e5ce1b444eb..5d2d02473ec 100644 --- a/.github/workflows/cluster_endtoend_onlineddl_scheduler.yml +++ b/.github/workflows/cluster_endtoend_onlineddl_scheduler.yml @@ -106,7 +106,7 @@ jobs: sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update # Install everything else we need, and configure - sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 + sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 sudo service mysql stop sudo service etcd stop diff --git a/.github/workflows/cluster_endtoend_onlineddl_vrepl.yml b/.github/workflows/cluster_endtoend_onlineddl_vrepl.yml index 6d2d14b72a4..f2d6f66d4e6 100644 --- a/.github/workflows/cluster_endtoend_onlineddl_vrepl.yml +++ b/.github/workflows/cluster_endtoend_onlineddl_vrepl.yml @@ -106,7 +106,7 @@ jobs: sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update # Install everything else we need, and configure - sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 + sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 sudo service mysql stop sudo service etcd stop diff --git a/.github/workflows/cluster_endtoend_onlineddl_vrepl_stress.yml b/.github/workflows/cluster_endtoend_onlineddl_vrepl_stress.yml index c91c87dc09f..a87dac04112 100644 --- a/.github/workflows/cluster_endtoend_onlineddl_vrepl_stress.yml +++ b/.github/workflows/cluster_endtoend_onlineddl_vrepl_stress.yml @@ -106,7 +106,7 @@ jobs: sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update # Install everything else we need, and configure - sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 + sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 sudo service mysql stop sudo service etcd stop diff --git a/.github/workflows/cluster_endtoend_onlineddl_vrepl_stress_suite.yml b/.github/workflows/cluster_endtoend_onlineddl_vrepl_stress_suite.yml index 18a3273bce2..afbc3bf11da 100644 --- a/.github/workflows/cluster_endtoend_onlineddl_vrepl_stress_suite.yml +++ b/.github/workflows/cluster_endtoend_onlineddl_vrepl_stress_suite.yml @@ -106,7 +106,7 @@ jobs: sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update # Install everything else we need, and configure - sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 + sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 sudo service mysql stop sudo service etcd stop diff --git a/.github/workflows/cluster_endtoend_onlineddl_vrepl_suite.yml b/.github/workflows/cluster_endtoend_onlineddl_vrepl_suite.yml index 0bdf2c03156..0cd80752a53 100644 --- a/.github/workflows/cluster_endtoend_onlineddl_vrepl_suite.yml +++ b/.github/workflows/cluster_endtoend_onlineddl_vrepl_suite.yml @@ -106,7 +106,7 @@ jobs: sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update # Install everything else we need, and configure - sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 + sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 sudo service mysql stop sudo service etcd stop diff --git a/.github/workflows/cluster_endtoend_schemadiff_vrepl.yml b/.github/workflows/cluster_endtoend_schemadiff_vrepl.yml index 0648fca17a5..04b4606e3bc 100644 --- a/.github/workflows/cluster_endtoend_schemadiff_vrepl.yml +++ b/.github/workflows/cluster_endtoend_schemadiff_vrepl.yml @@ -106,7 +106,7 @@ jobs: sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update # Install everything else we need, and configure - sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 + sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 sudo service mysql stop sudo service etcd stop diff --git a/.github/workflows/cluster_endtoend_tabletmanager_consul.yml b/.github/workflows/cluster_endtoend_tabletmanager_consul.yml index 7ff6800277f..34aeabf2aee 100644 --- a/.github/workflows/cluster_endtoend_tabletmanager_consul.yml +++ b/.github/workflows/cluster_endtoend_tabletmanager_consul.yml @@ -105,7 +105,7 @@ jobs: sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update # Install everything else we need, and configure - sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 + sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 sudo service mysql stop sudo service etcd stop diff --git a/.github/workflows/cluster_endtoend_tabletmanager_tablegc.yml b/.github/workflows/cluster_endtoend_tabletmanager_tablegc.yml index d70aedf1430..c712bd95e13 100644 --- a/.github/workflows/cluster_endtoend_tabletmanager_tablegc.yml +++ b/.github/workflows/cluster_endtoend_tabletmanager_tablegc.yml @@ -105,7 +105,7 @@ jobs: sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update # Install everything else we need, and configure - sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 + sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 sudo service mysql stop sudo service etcd stop diff --git a/.github/workflows/cluster_endtoend_tabletmanager_throttler_topo.yml b/.github/workflows/cluster_endtoend_tabletmanager_throttler_topo.yml index 35c5cba9c6b..fbc0082da7e 100644 --- a/.github/workflows/cluster_endtoend_tabletmanager_throttler_topo.yml +++ b/.github/workflows/cluster_endtoend_tabletmanager_throttler_topo.yml @@ -105,7 +105,7 @@ jobs: sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update # Install everything else we need, and configure - sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 + sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 sudo service mysql stop sudo service etcd stop diff --git a/.github/workflows/cluster_endtoend_topo_connection_cache.yml b/.github/workflows/cluster_endtoend_topo_connection_cache.yml index 7f2d1c4760e..5ba50c31ce3 100644 --- a/.github/workflows/cluster_endtoend_topo_connection_cache.yml +++ b/.github/workflows/cluster_endtoend_topo_connection_cache.yml @@ -105,7 +105,7 @@ jobs: sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update # Install everything else we need, and configure - sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 + sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 sudo service mysql stop sudo service etcd stop diff --git a/.github/workflows/cluster_endtoend_vreplication_across_db_versions.yml b/.github/workflows/cluster_endtoend_vreplication_across_db_versions.yml index b545dfbe1c9..3b4c0b70c57 100644 --- a/.github/workflows/cluster_endtoend_vreplication_across_db_versions.yml +++ b/.github/workflows/cluster_endtoend_vreplication_across_db_versions.yml @@ -105,7 +105,7 @@ jobs: sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update # Install everything else we need, and configure - sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 + sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 sudo service mysql stop sudo service etcd stop diff --git a/.github/workflows/cluster_endtoend_vreplication_basic.yml b/.github/workflows/cluster_endtoend_vreplication_basic.yml index 4ce0e941dba..7c1e41e6259 100644 --- a/.github/workflows/cluster_endtoend_vreplication_basic.yml +++ b/.github/workflows/cluster_endtoend_vreplication_basic.yml @@ -105,7 +105,7 @@ jobs: sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update # Install everything else we need, and configure - sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 + sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 sudo service mysql stop sudo service etcd stop diff --git a/.github/workflows/cluster_endtoend_vreplication_cellalias.yml b/.github/workflows/cluster_endtoend_vreplication_cellalias.yml index 4258d0484dc..fc5d87e8e2b 100644 --- a/.github/workflows/cluster_endtoend_vreplication_cellalias.yml +++ b/.github/workflows/cluster_endtoend_vreplication_cellalias.yml @@ -105,7 +105,7 @@ jobs: sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update # Install everything else we need, and configure - sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 + sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 sudo service mysql stop sudo service etcd stop diff --git a/.github/workflows/cluster_endtoend_vreplication_foreign_key_stress.yml b/.github/workflows/cluster_endtoend_vreplication_foreign_key_stress.yml index 35dc9f58ed5..3de3ed24be6 100644 --- a/.github/workflows/cluster_endtoend_vreplication_foreign_key_stress.yml +++ b/.github/workflows/cluster_endtoend_vreplication_foreign_key_stress.yml @@ -105,7 +105,7 @@ jobs: sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update # Install everything else we need, and configure - sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 + sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 sudo service mysql stop sudo service etcd stop diff --git a/.github/workflows/cluster_endtoend_vreplication_mariadb_to_mysql.yml b/.github/workflows/cluster_endtoend_vreplication_mariadb_to_mysql.yml new file mode 100644 index 00000000000..398d7a561f2 --- /dev/null +++ b/.github/workflows/cluster_endtoend_vreplication_mariadb_to_mysql.yml @@ -0,0 +1,179 @@ +# DO NOT MODIFY: THIS FILE IS GENERATED USING "make generate_ci_workflows" + +name: Cluster (vreplication_mariadb_to_mysql) +on: [push, pull_request] +concurrency: + group: format('{0}-{1}', ${{ github.ref }}, 'Cluster (vreplication_mariadb_to_mysql)') + cancel-in-progress: true + +permissions: read-all + +env: + LAUNCHABLE_ORGANIZATION: "vitess" + LAUNCHABLE_WORKSPACE: "vitess-app" + GITHUB_PR_HEAD_SHA: "${{ github.event.pull_request.head.sha }}" + +jobs: + build: + name: Run endtoend tests on Cluster (vreplication_mariadb_to_mysql) + runs-on: ubuntu-latest + + steps: + - name: Skip CI + run: | + if [[ "${{contains( github.event.pull_request.labels.*.name, 'Skip CI')}}" == "true" ]]; then + echo "skipping CI due to the 'Skip CI' label" + exit 1 + fi + + - name: Check if workflow needs to be skipped + id: skip-workflow + run: | + skip='false' + if [[ "${{github.event.pull_request}}" == "" ]] && [[ "${{github.ref}}" != "refs/heads/main" ]] && [[ ! "${{github.ref}}" =~ ^refs/heads/release-[0-9]+\.[0-9]$ ]] && [[ ! "${{github.ref}}" =~ "refs/tags/.*" ]]; then + skip='true' + fi + echo Skip ${skip} + echo "skip-workflow=${skip}" >> $GITHUB_OUTPUT + + PR_DATA=$(curl -s\ + -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \ + -H "Accept: application/vnd.github.v3+json" \ + "https://api.github.com/repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}") + draft=$(echo "$PR_DATA" | jq .draft -r) + echo "is_draft=${draft}" >> $GITHUB_OUTPUT + + - name: Check out code + if: steps.skip-workflow.outputs.skip-workflow == 'false' + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + + - name: Check for changes in relevant files + if: steps.skip-workflow.outputs.skip-workflow == 'false' + uses: dorny/paths-filter@ebc4d7e9ebcb0b1eb21480bb8f43113e996ac77a # v3.0.1 + id: changes + with: + token: '' + filters: | + end_to_end: + - 'go/**/*.go' + - 'go/vt/sidecardb/**/*.sql' + - 'go/test/endtoend/onlineddl/vrepl_suite/**' + - 'test.go' + - 'Makefile' + - 'build.env' + - 'go.sum' + - 'go.mod' + - 'proto/*.proto' + - 'tools/**' + - 'config/**' + - 'bootstrap.sh' + - '.github/workflows/cluster_endtoend_vreplication_mariadb_to_mysql.yml' + + - name: Set up Go + if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' + uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 + with: + go-version: 1.23.1 + + - name: Set up python + if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' + uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f # v5.1.1 + + - name: Tune the OS + if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' + run: | + # Limit local port range to not use ports that overlap with server side + # ports that we listen on. + sudo sysctl -w net.ipv4.ip_local_port_range="22768 65535" + # Increase the asynchronous non-blocking I/O. More information at https://dev.mysql.com/doc/refman/5.7/en/innodb-parameters.html#sysvar_innodb_use_native_aio + echo "fs.aio-max-nr = 1048576" | sudo tee -a /etc/sysctl.conf + sudo sysctl -p /etc/sysctl.conf + + - name: Get dependencies + if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' + run: | + + # Get key to latest MySQL repo + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C + # Setup MySQL 8.0 + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.32-1_all.deb + echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | sudo debconf-set-selections + sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* + sudo apt-get -qq update + # Install everything else we need, and configure + sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 + + sudo service mysql stop + sudo service etcd stop + sudo ln -s /etc/apparmor.d/usr.sbin.mysqld /etc/apparmor.d/disable/ + sudo apparmor_parser -R /etc/apparmor.d/usr.sbin.mysqld + go mod download + + # install JUnit report formatter + go install github.com/vitessio/go-junit-report@HEAD + + - name: Setup launchable dependencies + if: steps.skip-workflow.outputs.is_draft == 'false' && steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' && github.base_ref == 'main' + run: | + # Get Launchable CLI installed. If you can, make it a part of the builder image to speed things up + pip3 install --user launchable~=1.0 > /dev/null + + # verify that launchable setup is all correct. + launchable verify || true + + # Tell Launchable about the build you are producing and testing + launchable record build --name "$GITHUB_RUN_ID" --no-commit-collection --source . + + - name: Run cluster endtoend test + if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' + timeout-minutes: 45 + run: | + # We set the VTDATAROOT to the /tmp folder to reduce the file path of mysql.sock file + # which musn't be more than 107 characters long. + export VTDATAROOT="/tmp/" + source build.env + + set -exo pipefail + + # Increase our open file descriptor limit as we could hit this + ulimit -n 65536 + cat <<-EOF>>./config/mycnf/mysql8026.cnf + innodb_buffer_pool_dump_at_shutdown=OFF + innodb_buffer_pool_in_core_file=OFF + innodb_buffer_pool_load_at_startup=OFF + innodb_buffer_pool_size=64M + innodb_doublewrite=OFF + innodb_flush_log_at_trx_commit=0 + innodb_flush_method=O_DIRECT + innodb_numa_interleave=ON + innodb_adaptive_hash_index=OFF + sync_binlog=0 + sync_relay_log=0 + performance_schema=OFF + slow-query-log=OFF + EOF + + cat <<-EOF>>./config/mycnf/mysql8026.cnf + binlog-transaction-compression=ON + EOF + + # run the tests however you normally do, then produce a JUnit XML file + eatmydata -- go run test.go -docker=false -follow -shard vreplication_mariadb_to_mysql | tee -a output.txt | go-junit-report -set-exit-code > report.xml + + - name: Print test output and Record test result in launchable if PR is not a draft + if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' && always() + run: | + if [[ "${{steps.skip-workflow.outputs.is_draft}}" == "false" ]]; then + # send recorded tests to launchable + launchable record tests --build "$GITHUB_RUN_ID" go-test . || true + fi + + # print test output + cat output.txt + + - name: Test Summary + if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' && always() + uses: test-summary/action@31493c76ec9e7aa675f1585d3ed6f1da69269a86 # v2.4 + with: + paths: "report.xml" + show: "fail, skip" diff --git a/.github/workflows/cluster_endtoend_vreplication_migrate_vdiff2_convert_tz.yml b/.github/workflows/cluster_endtoend_vreplication_migrate_vdiff2_convert_tz.yml index 6f5aa22e95e..0104035d8bd 100644 --- a/.github/workflows/cluster_endtoend_vreplication_migrate_vdiff2_convert_tz.yml +++ b/.github/workflows/cluster_endtoend_vreplication_migrate_vdiff2_convert_tz.yml @@ -105,7 +105,7 @@ jobs: sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update # Install everything else we need, and configure - sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 + sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 sudo service mysql stop sudo service etcd stop diff --git a/.github/workflows/cluster_endtoend_vreplication_partial_movetables_and_materialize.yml b/.github/workflows/cluster_endtoend_vreplication_partial_movetables_and_materialize.yml index 829c06e57a6..e94fbf19274 100644 --- a/.github/workflows/cluster_endtoend_vreplication_partial_movetables_and_materialize.yml +++ b/.github/workflows/cluster_endtoend_vreplication_partial_movetables_and_materialize.yml @@ -105,7 +105,7 @@ jobs: sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update # Install everything else we need, and configure - sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 + sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 sudo service mysql stop sudo service etcd stop diff --git a/.github/workflows/cluster_endtoend_vreplication_v2.yml b/.github/workflows/cluster_endtoend_vreplication_v2.yml index 30244171ef8..a82facb50fc 100644 --- a/.github/workflows/cluster_endtoend_vreplication_v2.yml +++ b/.github/workflows/cluster_endtoend_vreplication_v2.yml @@ -105,7 +105,7 @@ jobs: sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update # Install everything else we need, and configure - sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 + sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 sudo service mysql stop sudo service etcd stop diff --git a/.github/workflows/cluster_endtoend_vstream.yml b/.github/workflows/cluster_endtoend_vstream.yml index c81c36a641f..6845d5e6bc1 100644 --- a/.github/workflows/cluster_endtoend_vstream.yml +++ b/.github/workflows/cluster_endtoend_vstream.yml @@ -105,7 +105,7 @@ jobs: sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update # Install everything else we need, and configure - sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 + sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 sudo service mysql stop sudo service etcd stop diff --git a/.github/workflows/cluster_endtoend_vtbackup.yml b/.github/workflows/cluster_endtoend_vtbackup.yml index 81b2ff49d2d..6f3ad6fa2e2 100644 --- a/.github/workflows/cluster_endtoend_vtbackup.yml +++ b/.github/workflows/cluster_endtoend_vtbackup.yml @@ -105,7 +105,7 @@ jobs: sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update # Install everything else we need, and configure - sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 + sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 sudo service mysql stop sudo service etcd stop diff --git a/.github/workflows/cluster_endtoend_vtctlbackup_sharded_clustertest_heavy.yml b/.github/workflows/cluster_endtoend_vtctlbackup_sharded_clustertest_heavy.yml index 730b91fc2d3..72caf485917 100644 --- a/.github/workflows/cluster_endtoend_vtctlbackup_sharded_clustertest_heavy.yml +++ b/.github/workflows/cluster_endtoend_vtctlbackup_sharded_clustertest_heavy.yml @@ -105,7 +105,7 @@ jobs: sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update # Install everything else we need, and configure - sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 + sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 sudo service mysql stop sudo service etcd stop diff --git a/.github/workflows/cluster_endtoend_vtgate_concurrentdml.yml b/.github/workflows/cluster_endtoend_vtgate_concurrentdml.yml index 9576c15816f..383f3dddc85 100644 --- a/.github/workflows/cluster_endtoend_vtgate_concurrentdml.yml +++ b/.github/workflows/cluster_endtoend_vtgate_concurrentdml.yml @@ -105,7 +105,7 @@ jobs: sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update # Install everything else we need, and configure - sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 + sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 sudo service mysql stop sudo service etcd stop diff --git a/.github/workflows/cluster_endtoend_vtgate_foreignkey_stress.yml b/.github/workflows/cluster_endtoend_vtgate_foreignkey_stress.yml index 720ff666a15..b138cb3f945 100644 --- a/.github/workflows/cluster_endtoend_vtgate_foreignkey_stress.yml +++ b/.github/workflows/cluster_endtoend_vtgate_foreignkey_stress.yml @@ -105,7 +105,7 @@ jobs: sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update # Install everything else we need, and configure - sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 + sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 sudo service mysql stop sudo service etcd stop diff --git a/.github/workflows/cluster_endtoend_vtgate_gen4.yml b/.github/workflows/cluster_endtoend_vtgate_gen4.yml index abf05102c95..cb8561bce88 100644 --- a/.github/workflows/cluster_endtoend_vtgate_gen4.yml +++ b/.github/workflows/cluster_endtoend_vtgate_gen4.yml @@ -105,7 +105,7 @@ jobs: sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update # Install everything else we need, and configure - sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 + sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 sudo service mysql stop sudo service etcd stop diff --git a/.github/workflows/cluster_endtoend_vtgate_general_heavy.yml b/.github/workflows/cluster_endtoend_vtgate_general_heavy.yml index 585d277a419..b2a599a37be 100644 --- a/.github/workflows/cluster_endtoend_vtgate_general_heavy.yml +++ b/.github/workflows/cluster_endtoend_vtgate_general_heavy.yml @@ -105,7 +105,7 @@ jobs: sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update # Install everything else we need, and configure - sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 + sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 sudo service mysql stop sudo service etcd stop diff --git a/.github/workflows/cluster_endtoend_vtgate_godriver.yml b/.github/workflows/cluster_endtoend_vtgate_godriver.yml index 1144565c406..3a58c31c440 100644 --- a/.github/workflows/cluster_endtoend_vtgate_godriver.yml +++ b/.github/workflows/cluster_endtoend_vtgate_godriver.yml @@ -105,7 +105,7 @@ jobs: sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update # Install everything else we need, and configure - sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 + sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 sudo service mysql stop sudo service etcd stop diff --git a/.github/workflows/cluster_endtoend_vtgate_partial_keyspace.yml b/.github/workflows/cluster_endtoend_vtgate_partial_keyspace.yml index bf281932d70..d9dcee37385 100644 --- a/.github/workflows/cluster_endtoend_vtgate_partial_keyspace.yml +++ b/.github/workflows/cluster_endtoend_vtgate_partial_keyspace.yml @@ -105,7 +105,7 @@ jobs: sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update # Install everything else we need, and configure - sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 + sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 sudo service mysql stop sudo service etcd stop diff --git a/.github/workflows/cluster_endtoend_vtgate_queries.yml b/.github/workflows/cluster_endtoend_vtgate_queries.yml index a99ab389ae1..c5a58a54df0 100644 --- a/.github/workflows/cluster_endtoend_vtgate_queries.yml +++ b/.github/workflows/cluster_endtoend_vtgate_queries.yml @@ -105,7 +105,7 @@ jobs: sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update # Install everything else we need, and configure - sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 + sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 sudo service mysql stop sudo service etcd stop diff --git a/.github/workflows/cluster_endtoend_vtgate_readafterwrite.yml b/.github/workflows/cluster_endtoend_vtgate_readafterwrite.yml index bfe3b156fc0..0d7e4031c30 100644 --- a/.github/workflows/cluster_endtoend_vtgate_readafterwrite.yml +++ b/.github/workflows/cluster_endtoend_vtgate_readafterwrite.yml @@ -105,7 +105,7 @@ jobs: sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update # Install everything else we need, and configure - sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 + sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 sudo service mysql stop sudo service etcd stop diff --git a/.github/workflows/cluster_endtoend_vtgate_reservedconn.yml b/.github/workflows/cluster_endtoend_vtgate_reservedconn.yml index 4487e18be13..cd98fabb441 100644 --- a/.github/workflows/cluster_endtoend_vtgate_reservedconn.yml +++ b/.github/workflows/cluster_endtoend_vtgate_reservedconn.yml @@ -105,7 +105,7 @@ jobs: sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update # Install everything else we need, and configure - sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 + sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 sudo service mysql stop sudo service etcd stop diff --git a/.github/workflows/cluster_endtoend_vtgate_schema.yml b/.github/workflows/cluster_endtoend_vtgate_schema.yml index 7bb9bddb5f4..dc0b9d39be1 100644 --- a/.github/workflows/cluster_endtoend_vtgate_schema.yml +++ b/.github/workflows/cluster_endtoend_vtgate_schema.yml @@ -105,7 +105,7 @@ jobs: sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update # Install everything else we need, and configure - sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 + sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 sudo service mysql stop sudo service etcd stop diff --git a/.github/workflows/cluster_endtoend_vtgate_schema_tracker.yml b/.github/workflows/cluster_endtoend_vtgate_schema_tracker.yml index 858a6a2236b..22b4498e881 100644 --- a/.github/workflows/cluster_endtoend_vtgate_schema_tracker.yml +++ b/.github/workflows/cluster_endtoend_vtgate_schema_tracker.yml @@ -105,7 +105,7 @@ jobs: sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update # Install everything else we need, and configure - sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 + sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 sudo service mysql stop sudo service etcd stop diff --git a/.github/workflows/cluster_endtoend_vtgate_tablet_healthcheck_cache.yml b/.github/workflows/cluster_endtoend_vtgate_tablet_healthcheck_cache.yml index 6aabc077d1e..06b0b0dfbbb 100644 --- a/.github/workflows/cluster_endtoend_vtgate_tablet_healthcheck_cache.yml +++ b/.github/workflows/cluster_endtoend_vtgate_tablet_healthcheck_cache.yml @@ -105,7 +105,7 @@ jobs: sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update # Install everything else we need, and configure - sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 + sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 sudo service mysql stop sudo service etcd stop diff --git a/.github/workflows/cluster_endtoend_vtgate_topo.yml b/.github/workflows/cluster_endtoend_vtgate_topo.yml index 4150505b305..8b4515e7e17 100644 --- a/.github/workflows/cluster_endtoend_vtgate_topo.yml +++ b/.github/workflows/cluster_endtoend_vtgate_topo.yml @@ -105,7 +105,7 @@ jobs: sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update # Install everything else we need, and configure - sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 + sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 sudo service mysql stop sudo service etcd stop diff --git a/.github/workflows/cluster_endtoend_vtgate_topo_consul.yml b/.github/workflows/cluster_endtoend_vtgate_topo_consul.yml index cd389612d75..c957337f079 100644 --- a/.github/workflows/cluster_endtoend_vtgate_topo_consul.yml +++ b/.github/workflows/cluster_endtoend_vtgate_topo_consul.yml @@ -105,7 +105,7 @@ jobs: sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update # Install everything else we need, and configure - sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 + sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 sudo service mysql stop sudo service etcd stop diff --git a/.github/workflows/cluster_endtoend_vtgate_topo_etcd.yml b/.github/workflows/cluster_endtoend_vtgate_topo_etcd.yml index c51a149f104..ddd21b08de9 100644 --- a/.github/workflows/cluster_endtoend_vtgate_topo_etcd.yml +++ b/.github/workflows/cluster_endtoend_vtgate_topo_etcd.yml @@ -105,7 +105,7 @@ jobs: sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update # Install everything else we need, and configure - sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 + sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 sudo service mysql stop sudo service etcd stop diff --git a/.github/workflows/cluster_endtoend_vtgate_transaction.yml b/.github/workflows/cluster_endtoend_vtgate_transaction.yml index 2a01e5fd2b7..5abb227a4b9 100644 --- a/.github/workflows/cluster_endtoend_vtgate_transaction.yml +++ b/.github/workflows/cluster_endtoend_vtgate_transaction.yml @@ -105,7 +105,7 @@ jobs: sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update # Install everything else we need, and configure - sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 + sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 sudo service mysql stop sudo service etcd stop diff --git a/.github/workflows/cluster_endtoend_vtgate_unsharded.yml b/.github/workflows/cluster_endtoend_vtgate_unsharded.yml index 4440154b9cf..5c248b6e6fd 100644 --- a/.github/workflows/cluster_endtoend_vtgate_unsharded.yml +++ b/.github/workflows/cluster_endtoend_vtgate_unsharded.yml @@ -105,7 +105,7 @@ jobs: sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update # Install everything else we need, and configure - sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 + sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 sudo service mysql stop sudo service etcd stop diff --git a/.github/workflows/cluster_endtoend_vtgate_vindex_heavy.yml b/.github/workflows/cluster_endtoend_vtgate_vindex_heavy.yml index 74f5f376884..39aa1f1adb9 100644 --- a/.github/workflows/cluster_endtoend_vtgate_vindex_heavy.yml +++ b/.github/workflows/cluster_endtoend_vtgate_vindex_heavy.yml @@ -105,7 +105,7 @@ jobs: sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update # Install everything else we need, and configure - sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 + sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 sudo service mysql stop sudo service etcd stop diff --git a/.github/workflows/cluster_endtoend_vtgate_vschema.yml b/.github/workflows/cluster_endtoend_vtgate_vschema.yml index 82f196c6579..c6e8c27ae9d 100644 --- a/.github/workflows/cluster_endtoend_vtgate_vschema.yml +++ b/.github/workflows/cluster_endtoend_vtgate_vschema.yml @@ -105,7 +105,7 @@ jobs: sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update # Install everything else we need, and configure - sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 + sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 sudo service mysql stop sudo service etcd stop diff --git a/.github/workflows/cluster_endtoend_vtorc.yml b/.github/workflows/cluster_endtoend_vtorc.yml index 6aa56d4ec8b..deeb78abacf 100644 --- a/.github/workflows/cluster_endtoend_vtorc.yml +++ b/.github/workflows/cluster_endtoend_vtorc.yml @@ -105,7 +105,7 @@ jobs: sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update # Install everything else we need, and configure - sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 + sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 sudo service mysql stop sudo service etcd stop diff --git a/.github/workflows/cluster_endtoend_vttablet_prscomplex.yml b/.github/workflows/cluster_endtoend_vttablet_prscomplex.yml index 27bc12c7385..57237ce8dc7 100644 --- a/.github/workflows/cluster_endtoend_vttablet_prscomplex.yml +++ b/.github/workflows/cluster_endtoend_vttablet_prscomplex.yml @@ -105,7 +105,7 @@ jobs: sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update # Install everything else we need, and configure - sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 + sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 sudo service mysql stop sudo service etcd stop diff --git a/changelog/21.0/21.0.0/summary.md b/changelog/21.0/21.0.0/summary.md new file mode 100644 index 00000000000..03f138a60c7 --- /dev/null +++ b/changelog/21.0/21.0.0/summary.md @@ -0,0 +1,174 @@ +## Summary + +### Table of Contents + +- **[Major Changes](#major-changes)** + - **[Deprecations and Deletions](#deprecations-and-deletions)** + - [Deletion of deprecated metrics](#metric-deletion) + - [VTTablet Flags](#vttablet-flags) + - [Metrics](#deprecations-metrics) + - **[Traffic Mirroring](#traffic-mirroring)** + - **[New VTGate Shutdown Behavior](#new-vtgate-shutdown-behavior)** + - **[Tablet Throttler: Multi-Metric support](#tablet-throttler)** + - **[Allow Cross Cell Promotion in PRS](#allow-cross-cell)** + - **[Support for recursive CTEs](#recursive-cte)** + - **[VTGate Tablet Balancer](#tablet-balancer)** + - **[Query Timeout Override](#query-timeout)** + - **[New Backup Engine](#new-backup-engine)** + - **[Dynamic VReplication Configuration](#dynamic-vreplication-configuration)** + - **[Reference Table Materialization](#reference-table-materialization)** + +## Major Changes + +### Deprecations and Deletions + +#### Deletion of deprecated metrics + +The following metrics that were deprecated in the previous release, have now been deleted. + +| Metric Name | +|:--------------------------------------------:| +| `analysis.change.write` | +| `audit.write` | +| `discoveries.attempt` | +| `discoveries.fail` | +| `discoveries.instance_poll_seconds_exceeded` | +| `discoveries.queue_length` | +| `discoveries.recent_count` | +| `instance.read` | +| `instance.read_topology` | +| `emergency_reparent_counts` | +| `planned_reparent_counts` | +| `reparent_shard_operation_timings` | + +#### VTTablet Flags + +- `queryserver-enable-settings-pool` flag, added in v15, has been on by default since v17. + It is now deprecated and will be removed in a future release. + +#### Metrics + +The following metrics are now deprecated, if provided please use their replacement. + +| Component | Metric Name | Replaced By | +|------------|:---------------------:|:-------------------------------:| +| `vttablet` | `QueryCacheLength` | `QueryEnginePlanCacheLength` | +| `vttablet` | `QueryCacheSize` | `QueryEnginePlanCacheSize` | +| `vttablet` | `QueryCacheCapacity` | `QueryEnginePlanCacheCapacity` | +| `vttablet` | `QueryCacheEvictions` | `QueryEnginePlanCacheEvictions` | +| `vttablet` | `QueryCacheHits` | `QueryEnginePlanCacheHits` | +| `vttablet` | `QueryCacheMisses` | `QueryEnginePlanCacheMisses` | + +### Traffic Mirroring + +Traffic mirroring is intended to help reduce some of the uncertainty inherent to `MoveTables SwitchTraffic`. When +traffic mirroring is enabled, VTGate will mirror a percentage of traffic from one keyspace to another. + +Mirror rules may be enabled through `vtctldclient` with `MoveTables MirrorTraffic`. For example: + +```bash +$ vtctldclient --server :15999 MoveTables --target-keyspace customer --workflow commerce2customer MirrorTraffic --percent 5.0 +``` + +Mirror rules can be inspected with `GetMirrorRules`. + +### New VTGate Shutdown Behavior + +We added a new option to affect the VTGate shutdown process in v21 by using a connection drain timeout rather than the +older activity drain timeout. +The goal of this new behavior, connection draining option, is to disallow new connections when VTGate is shutting down, +but continue allowing existing connections to finish their work until they manually disconnect or until +the `--onterm_timeout` timeout is reached, +without getting a `Server shutdown in progress` error. + +This new behavior can be enabled by specifying the new `--mysql-server-drain-onterm` flag to VTGate. + +See more information about this change by [reading its RFC](https://github.com/vitessio/vitess/issues/15971). + +### Tablet Throttler: Multi-Metric support + +Up till `v20`, the tablet throttler would only monitor and use a single metric. That would be replication lag, by +default, or could be the result of a custom query. `v21` introduces a major redesign where the throttler monitors and +uses multiple metrics at the same time, including the above two. + +Backwards compatible with `v20`, the default behavior in `v21` is to monitor all metrics, but only use `lag` (if the +cutsom query is undefined) or the `cutsom` metric (if the custom query is defined). A `v20` `PRIMARY` is compatible with +a `v21` `REPLICA`, and a `v21` `PRIMARY` is compatible with a `v20` `REPLICA`. + +However, with `v21` it is possible to assign any combination of metrics (one or more) for a given app. The throttler +would then accept or reject the app's requests based on the health of _all_ assigned metrics. `v21` comes with a preset +list metrics, expected to be expanded: + +- `lag`: replication lag based on heartbeat injection. +- `threads_running`: concurrent active threads on the MySQL server. +- `loadavg`: per core load average measured on the tablet instance/pod. +- `custom`: the result of a custom query executed on the MySQL server. + +Each metric has a factory threshold which can be overridden by the `UpdateThrottlerConfig` command. + +The throttler also supports the catch-all `"all"` app name, and it is thus possible to assign metrics to _all_ apps. +Explicit app to metric assignments will override the catch-all configuration. + +Metrics are assigned a default _scope_, which could be `self` (isolated to the tablet) or `shard` (max, aka _worst_ +value among shard tablets). It is further possible to require a different scope for each metric. + +### Allow Cross Cell Promotion in PRS + +Up until now if the users wanted to promote a replica in a different cell than the current primary +using `PlannedReparentShard`, they had to specify the new primary with the `--new-primary` flag. + +We have now added a new flag `--allow-cross-cell-promotion` that lets `PlannedReparentShard` choose a primary in a +different cell even if no new primary is provided explicitly. + +### Experimental support for recursive CTEs + +We have added experimental support for recursive CTEs in Vitess. We are marking it as experimental because it is not yet +fully tested and may have some limitations. We are looking for feedback from the community to improve this feature. + +### VTGate Tablet Balancer + +When a VTGate routes a query and has multiple available tablets for a given shard / tablet type (e.g. REPLICA), the +current default behavior routes the query with local cell affinity and round robin policy. The VTGate Tablet Balancer +provides an alternate mechanism that routes queries to maintain an even distribution of query load to each tablet, while +preferentially routing to tablets in the same cell as the VTGate. + +The tablet balancer is enabled by a new flag `--enable-balancer` and configured by `--balancer-vtgate-cells` +and `--balancer-keyspaces`. + +See [RFC for details](https://github.com/vitessio/vitess/issues/12241). + +### Query Timeout Override + +VTGate sends an authoritative query timeout to VTTablet when the `QUERY_TIMEOUT_MS` comment directive, +`query_timeout` session system variable, or `query-timeout` flag is set. +The order of precedence is: `QUERY_TIMEOUT_MS` > `query_timeout` > `query-timeout`. +VTTablet overrides its default query timeout with the value received from VTGate. +All timeouts are specified in milliseconds. + +When a query is executed inside a transaction, this behavior does not apply; instead, +the smaller of the transaction timeout or the query timeout from VTGate is used. + +A query can also be set to have no timeout by using the `QUERY_TIMEOUT_MS` comment directive with a value of `0`. + +Example usage: +`select /*vt+ QUERY_TIMEOUT_MS=30 */ col from tbl` + +### New Backup Engine (EXPERIMENTAL) + +We are introducing a backup engine supporting logical backups starting on v21 to support use cases that require something else besides physical backups. This is experimental and is based on the +[MySQL Shell](https://dev.mysql.com/doc/mysql-shell/8.0/en/). + +The new engine is enabled by using `--backup_engine_implementation=mysqlshell`. There are other options that are required, so [check the docs](https://vitess.io/docs/21.0/user-guides/operating-vitess/backup-and-restore/creating-a-backup/) on which options are required and how to use it. + +### Dynamic VReplication Configuration + +Currently many of the configuration options for VReplication Workflows are vttablet flags. This means that any change +requires restarts of vttablets. We now allow these to be overridden while creating a workflow or dynamically once +the workflow is in progress. See https://github.com/vitessio/vitess/pull/16583 for details. + +### Reference Table Materialization + +There is a new option in [`Materialize` workflows](https://vitess.io/docs/reference/vreplication/materialize/) to keep +a synced copy of [reference or lookup tables](https://vitess.io/docs/reference/vreplication/reference_tables/) +(countries, states, zip_codes, etc) from an unsharded keyspace, which holds the source of truth for the reference +table, to all shards in a sharded keyspace. diff --git a/config/init_db.sql b/config/init_db.sql index d04960633de..7698c9bd1d8 100644 --- a/config/init_db.sql +++ b/config/init_db.sql @@ -37,6 +37,7 @@ DROP DATABASE IF EXISTS test; CREATE USER 'vt_dba'@'localhost'; GRANT ALL ON *.* TO 'vt_dba'@'localhost'; GRANT GRANT OPTION ON *.* TO 'vt_dba'@'localhost'; +GRANT PROXY ON ''@'' TO 'vt_dba'@'localhost' WITH GRANT OPTION; # User for app traffic, with global read-write access. CREATE USER 'vt_app'@'localhost'; diff --git a/docker/lite/Dockerfile~969e018d8d (adding new mysql shell backup engine (#16295)) b/docker/lite/Dockerfile~969e018d8d (adding new mysql shell backup engine (#16295)) new file mode 100644 index 00000000000..9d2b90efda2 --- /dev/null +++ b/docker/lite/Dockerfile~969e018d8d (adding new mysql shell backup engine (#16295)) @@ -0,0 +1,63 @@ +# Copyright 2019 The Vitess Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +FROM --platform=linux/amd64 golang:1.23.1-bullseye AS builder + +# Allows docker builds to set the BUILD_NUMBER +ARG BUILD_NUMBER + +WORKDIR /vt/src/vitess.io/vitess + +# Create vitess user +RUN groupadd -r vitess && useradd -r -g vitess vitess +RUN mkdir -p /vt/vtdataroot /home/vitess +RUN chown -R vitess:vitess /vt /home/vitess +USER vitess + +# Re-copy sources from working tree. +COPY --chown=vitess:vitess . /vt/src/vitess.io/vitess + +RUN make install PREFIX=/vt/install + +# Start over and build the final image. +FROM --platform=linux/amd64 debian:bullseye-slim + +# Install locale required for mysqlsh +RUN apt-get update && apt-get install -y locales \ + && echo "en_US.UTF-8 UTF-8" > /etc/locale.gen \ + && locale-gen en_US.UTF-8 + +# Install dependencies +COPY docker/utils/install_dependencies.sh /vt/dist/install_dependencies.sh +RUN /vt/dist/install_dependencies.sh mysql80 + +# Set up Vitess user and directory tree. +RUN groupadd -r vitess && useradd -r -g vitess vitess +RUN mkdir -p /vt/vtdataroot /home/vitess && chown -R vitess:vitess /vt /home/vitess + +# Set up Vitess environment (just enough to run pre-built Go binaries) +ENV VTROOT /vt +ENV VTDATAROOT /vt/vtdataroot +ENV PATH $VTROOT/bin:$PATH + +# Copy artifacts from builder layer. +COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt +COPY --from=builder --chown=vitess:vitess /vt/install /vt +COPY --from=builder --chown=vitess:vitess /vt/src/vitess.io/vitess/web/vtadmin /vt/web/vtadmin +COPY --from=builder --chown=vitess:vitess /vt/src/vitess.io/vitess/config/init_db.sql /vt/config/ +COPY --from=builder --chown=vitess:vitess /vt/src/vitess.io/vitess/config/mycnf /vt/config/ + +# Create mount point for actual data (e.g. MySQL data dir) +VOLUME /vt/vtdataroot +USER vitess diff --git a/docker/lite/install_dependencies.sh b/docker/lite/install_dependencies.sh index b686c2418bf..91e6e2b8c76 100755 --- a/docker/lite/install_dependencies.sh +++ b/docker/lite/install_dependencies.sh @@ -86,6 +86,7 @@ mysql57) /tmp/mysql-client_${VERSION}-1debian10_amd64.deb /tmp/mysql-community-server_${VERSION}-1debian10_amd64.deb /tmp/mysql-server_${VERSION}-1debian10_amd64.deb + mysql-shell percona-xtrabackup-24 ) ;; @@ -112,6 +113,7 @@ mysql80) /tmp/mysql-community-server-core_${VERSION}-1debian11_amd64.deb /tmp/mysql-community-server_${VERSION}-1debian11_amd64.deb /tmp/mysql-server_${VERSION}-1debian11_amd64.deb + mysql-shell percona-xtrabackup-80 ) ;; diff --git a/examples/compose/config/init_db.sql b/examples/compose/config/init_db.sql index 8239d5ed5ec..b780655c048 100644 --- a/examples/compose/config/init_db.sql +++ b/examples/compose/config/init_db.sql @@ -41,6 +41,7 @@ CREATE TABLE IF NOT EXISTS _vt.shard_metadata ( CREATE USER 'vt_dba'@'localhost'; GRANT ALL ON *.* TO 'vt_dba'@'localhost'; GRANT GRANT OPTION ON *.* TO 'vt_dba'@'localhost'; +GRANT PROXY ON ''@'' TO 'vt_dba'@'localhost' WITH GRANT OPTION; # User for app traffic, with global read-write access. CREATE USER 'vt_app'@'localhost'; GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, RELOAD, PROCESS, FILE, diff --git a/examples/operator/101_initial_cluster.yaml b/examples/operator/101_initial_cluster.yaml index 7df159a56f9..a9e1bc28007 100644 --- a/examples/operator/101_initial_cluster.yaml +++ b/examples/operator/101_initial_cluster.yaml @@ -179,6 +179,7 @@ stringData: CREATE USER 'vt_dba'@'localhost'; GRANT ALL ON *.* TO 'vt_dba'@'localhost'; GRANT GRANT OPTION ON *.* TO 'vt_dba'@'localhost'; + GRANT PROXY ON ''@'' TO 'vt_dba'@'localhost' WITH GRANT OPTION; # User for app traffic, with global read-write access. CREATE USER 'vt_app'@'localhost'; diff --git a/examples/operator/operator.yaml b/examples/operator/operator.yaml index b5b5c97b6cb..d612b47f4de 100644 --- a/examples/operator/operator.yaml +++ b/examples/operator/operator.yaml @@ -1247,6 +1247,7 @@ spec: enum: - builtin - xtrabackup + - mysqlshell type: string locations: items: diff --git a/go/flags/endtoend/vtbackup.txt b/go/flags/endtoend/vtbackup.txt index 2926224fa35..25730ab892f 100644 --- a/go/flags/endtoend/vtbackup.txt +++ b/go/flags/endtoend/vtbackup.txt @@ -177,6 +177,12 @@ Flags: --mycnf_slow_log_path string mysql slow query log path --mycnf_socket_file string mysql socket file --mycnf_tmp_dir string mysql tmp directory + --mysql-shell-backup-location string location where the backup will be stored + --mysql-shell-dump-flags string flags to pass to mysql shell dump utility. This should be a JSON string and will be saved in the MANIFEST (default "{\"threads\": 4}") + --mysql-shell-flags string execution flags to pass to mysqlsh binary to be used during dump/load (default "--defaults-file=/dev/null --js -h localhost") + --mysql-shell-load-flags string flags to pass to mysql shell load utility. This should be a JSON string (default "{\"threads\": 4, \"loadUsers\": true, \"updateGtidSet\": \"replace\", \"skipBinlog\": true, \"progressFile\": \"\"}") + --mysql-shell-should-drain decide if we should drain while taking a backup or continue to serving traffic + --mysql-shell-speedup-restore speed up restore by disabling redo logging and double write buffer during the restore process --mysql-shutdown-timeout duration how long to wait for mysqld shutdown (default 5m0s) --mysql_port int mysql port (default 3306) --mysql_server_version string MySQL server version to advertise. (default "8.0.30-Vitess") diff --git a/go/flags/endtoend/vtcombo.txt b/go/flags/endtoend/vtcombo.txt index 0feaef950b8..ea7f48e6ae5 100644 --- a/go/flags/endtoend/vtcombo.txt +++ b/go/flags/endtoend/vtcombo.txt @@ -226,6 +226,12 @@ Flags: --mycnf_tmp_dir string mysql tmp directory --mysql-server-keepalive-period duration TCP period between keep-alives --mysql-server-pool-conn-read-buffers If set, the server will pool incoming connection read buffers + --mysql-shell-backup-location string location where the backup will be stored + --mysql-shell-dump-flags string flags to pass to mysql shell dump utility. This should be a JSON string and will be saved in the MANIFEST (default "{\"threads\": 4}") + --mysql-shell-flags string execution flags to pass to mysqlsh binary to be used during dump/load (default "--defaults-file=/dev/null --js -h localhost") + --mysql-shell-load-flags string flags to pass to mysql shell load utility. This should be a JSON string (default "{\"threads\": 4, \"loadUsers\": true, \"updateGtidSet\": \"replace\", \"skipBinlog\": true, \"progressFile\": \"\"}") + --mysql-shell-should-drain decide if we should drain while taking a backup or continue to serving traffic + --mysql-shell-speedup-restore speed up restore by disabling redo logging and double write buffer during the restore process --mysql-shutdown-timeout duration timeout to use when MySQL is being shut down. (default 5m0s) --mysql_allow_clear_text_without_tls If set, the server will allow the use of a clear text password over non-SSL connections. --mysql_auth_server_impl string Which auth server implementation to use. Options: none, ldap, clientcert, static, vault. (default "static") diff --git a/go/flags/endtoend/vttablet.txt b/go/flags/endtoend/vttablet.txt index f6a903817d8..f9d8a9a796d 100644 --- a/go/flags/endtoend/vttablet.txt +++ b/go/flags/endtoend/vttablet.txt @@ -244,6 +244,12 @@ Flags: --mycnf_slow_log_path string mysql slow query log path --mycnf_socket_file string mysql socket file --mycnf_tmp_dir string mysql tmp directory + --mysql-shell-backup-location string location where the backup will be stored + --mysql-shell-dump-flags string flags to pass to mysql shell dump utility. This should be a JSON string and will be saved in the MANIFEST (default "{\"threads\": 4}") + --mysql-shell-flags string execution flags to pass to mysqlsh binary to be used during dump/load (default "--defaults-file=/dev/null --js -h localhost") + --mysql-shell-load-flags string flags to pass to mysql shell load utility. This should be a JSON string (default "{\"threads\": 4, \"loadUsers\": true, \"updateGtidSet\": \"replace\", \"skipBinlog\": true, \"progressFile\": \"\"}") + --mysql-shell-should-drain decide if we should drain while taking a backup or continue to serving traffic + --mysql-shell-speedup-restore speed up restore by disabling redo logging and double write buffer during the restore process --mysql-shutdown-timeout duration timeout to use when MySQL is being shut down. (default 5m0s) --mysql_server_version string MySQL server version to advertise. (default "8.0.30-Vitess") --mysqlctl_mycnf_template string template file to use for generating the my.cnf file during server init diff --git a/go/flags/endtoend/vttestserver.txt b/go/flags/endtoend/vttestserver.txt index c9a1434f17c..205cd8aee3d 100644 --- a/go/flags/endtoend/vttestserver.txt +++ b/go/flags/endtoend/vttestserver.txt @@ -87,6 +87,12 @@ Flags: --max-stack-size int configure the maximum stack size in bytes (default 67108864) --max_table_shard_size int The maximum number of initial rows in a table shard. Ignored if--initialize_with_random_data is false. The actual number is chosen randomly (default 10000) --min_table_shard_size int The minimum number of initial rows in a table shard. Ignored if--initialize_with_random_data is false. The actual number is chosen randomly. (default 1000) + --mysql-shell-backup-location string location where the backup will be stored + --mysql-shell-dump-flags string flags to pass to mysql shell dump utility. This should be a JSON string and will be saved in the MANIFEST (default "{\"threads\": 4}") + --mysql-shell-flags string execution flags to pass to mysqlsh binary to be used during dump/load (default "--defaults-file=/dev/null --js -h localhost") + --mysql-shell-load-flags string flags to pass to mysql shell load utility. This should be a JSON string (default "{\"threads\": 4, \"loadUsers\": true, \"updateGtidSet\": \"replace\", \"skipBinlog\": true, \"progressFile\": \"\"}") + --mysql-shell-should-drain decide if we should drain while taking a backup or continue to serving traffic + --mysql-shell-speedup-restore speed up restore by disabling redo logging and double write buffer during the restore process --mysql_bind_host string which host to bind vtgate mysql listener to (default "localhost") --mysql_only If this flag is set only mysql is initialized. The rest of the vitess components are not started. Also, the output specifies the mysql unix socket instead of the vtgate port. --mysql_server_version string MySQL server version to advertise. (default "8.0.30-Vitess") diff --git a/go/test/endtoend/backup/pitr_mysqlshell/backup_pitr_mysqlshell_test.go b/go/test/endtoend/backup/pitr_mysqlshell/backup_pitr_mysqlshell_test.go new file mode 100644 index 00000000000..fe6fa0b7dcd --- /dev/null +++ b/go/test/endtoend/backup/pitr_mysqlshell/backup_pitr_mysqlshell_test.go @@ -0,0 +1,61 @@ +/* +Copyright 2024 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package mysqlctld + +import ( + "testing" + + backup "vitess.io/vitess/go/test/endtoend/backup/vtctlbackup" +) + +// TestIncrementalBackupAndRestoreToPos +func TestIncrementalBackupAndRestoreToPos(t *testing.T) { + tcase := &backup.PITRTestCase{ + Name: "MySQLShell", + SetupType: backup.MySQLShell, + ComprssDetails: &backup.CompressionDetails{ + CompressorEngineName: "pgzip", + }, + } + backup.ExecTestIncrementalBackupAndRestoreToPos(t, tcase) +} + +// TestIncrementalBackupAndRestoreToTimestamp - tests incremental backups and restores. +// The general outline of the test: +// - Generate some schema with data +// - Take a full backup +// - Proceed to take a series of inremental backups. In between, inject data (insert rows), and keep record +// of which data (number of rows) is present in each backup, and at which timestamp. +// - Expect backups success/failure per scenario +// - Next up, we start testing restores. Randomly pick recorded timestamps and restore to those points in time. +// - In each restore, excpect to find the data (number of rows) recorded for said timestamp +// - Some restores should fail because the timestamp exceeds the last binlog +// - Do so for all recorded tiemstamps. +// - Then, a 2nd round where some backups are purged -- this tests to see that we're still able to find a restore path +// (of course we only delete backups that still leave us with valid restore paths). +// +// All of the above is done for BuiltinBackup, XtraBackup, Mysqlctld (which is technically builtin) +func TestIncrementalBackupAndRestoreToTimestamp(t *testing.T) { + tcase := &backup.PITRTestCase{ + Name: "MySQLShell", + SetupType: backup.MySQLShell, + ComprssDetails: &backup.CompressionDetails{ + CompressorEngineName: "pgzip", + }, + } + backup.ExecTestIncrementalBackupAndRestoreToTimestamp(t, tcase) +} diff --git a/go/test/endtoend/backup/vtctlbackup/backup_utils.go b/go/test/endtoend/backup/vtctlbackup/backup_utils.go index a70d1804028..e2c1e17bebe 100644 --- a/go/test/endtoend/backup/vtctlbackup/backup_utils.go +++ b/go/test/endtoend/backup/vtctlbackup/backup_utils.go @@ -53,19 +53,20 @@ const ( XtraBackup = iota BuiltinBackup Mysqlctld + MySQLShell timeout = time.Duration(60 * time.Second) topoConsistencyTimeout = 20 * time.Second ) var ( - primary *cluster.Vttablet - replica1 *cluster.Vttablet - replica2 *cluster.Vttablet - replica3 *cluster.Vttablet - localCluster *cluster.LocalProcessCluster - newInitDBFile string - useXtrabackup bool - cell = cluster.DefaultCell + primary *cluster.Vttablet + replica1 *cluster.Vttablet + replica2 *cluster.Vttablet + replica3 *cluster.Vttablet + localCluster *cluster.LocalProcessCluster + newInitDBFile string + currentSetupType int + cell = cluster.DefaultCell hostname = "localhost" keyspaceName = "ks" @@ -102,6 +103,7 @@ type CompressionDetails struct { // LaunchCluster : starts the cluster as per given params. func LaunchCluster(setupType int, streamMode string, stripes int, cDetails *CompressionDetails) (int, error) { + currentSetupType = setupType localCluster = cluster.NewCluster(cell, hostname) // Start topo server @@ -143,10 +145,9 @@ func LaunchCluster(setupType int, streamMode string, stripes int, cDetails *Comp extraArgs := []string{"--db-credentials-file", dbCredentialFile} commonTabletArg = append(commonTabletArg, "--db-credentials-file", dbCredentialFile) - // Update arguments for xtrabackup - if setupType == XtraBackup { - useXtrabackup = true - + // Update arguments for different backup engines + switch setupType { + case XtraBackup: xtrabackupArgs := []string{ "--backup_engine_implementation", "xtrabackup", fmt.Sprintf("--xtrabackup_stream_mode=%s", streamMode), @@ -161,6 +162,18 @@ func LaunchCluster(setupType int, streamMode string, stripes int, cDetails *Comp } commonTabletArg = append(commonTabletArg, xtrabackupArgs...) + case MySQLShell: + mysqlShellBackupLocation := path.Join(localCluster.CurrentVTDATAROOT, "backups-mysqlshell") + err = os.MkdirAll(mysqlShellBackupLocation, 0o777) + if err != nil { + return 0, err + } + + mysqlShellArgs := []string{ + "--backup_engine_implementation", "mysqlshell", + "--mysql-shell-backup-location", mysqlShellBackupLocation, + } + commonTabletArg = append(commonTabletArg, mysqlShellArgs...) } commonTabletArg = append(commonTabletArg, getCompressorArgs(cDetails)...) @@ -177,9 +190,19 @@ func LaunchCluster(setupType int, streamMode string, stripes int, cDetails *Comp tablet := localCluster.NewVttabletInstance(tabletType, 0, cell) tablet.VttabletProcess = localCluster.VtprocessInstanceFromVttablet(tablet, shard.Name, keyspaceName) tablet.VttabletProcess.DbPassword = dbPassword - tablet.VttabletProcess.ExtraArgs = commonTabletArg tablet.VttabletProcess.SupportsBackup = true + // since we spin different mysqld processes, we need to pass exactly the socket of the this particular + // one when running mysql shell dump/loads + if setupType == MySQLShell { + commonTabletArg = append(commonTabletArg, + "--mysql-shell-flags", fmt.Sprintf("--js -u vt_dba -p%s -S %s", dbPassword, + path.Join(os.Getenv("VTDATAROOT"), fmt.Sprintf("/vt_%010d", tablet.TabletUID), "mysql.sock"), + ), + ) + } + tablet.VttabletProcess.ExtraArgs = commonTabletArg + if setupType == Mysqlctld { mysqlctldProcess, err := cluster.MysqlCtldProcessInstance(tablet.TabletUID, tablet.MySQLPort, localCluster.TmpDirectory) if err != nil { @@ -1024,13 +1047,9 @@ func verifySemiSyncStatus(t *testing.T, vttablet *cluster.Vttablet, expectedStat } func terminateBackup(t *testing.T, alias string) { - stopBackupMsg := "Done taking Backup" - if useXtrabackup { + stopBackupMsg := "Completed backing up" + if currentSetupType == XtraBackup { stopBackupMsg = "Starting backup with" - useXtrabackup = false - defer func() { - useXtrabackup = true - }() } args := append([]string{"--server", localCluster.VtctlclientProcess.Server, "--alsologtostderr"}, "Backup", "--", alias) @@ -1059,12 +1078,8 @@ func terminateBackup(t *testing.T, alias string) { func terminateRestore(t *testing.T) { stopRestoreMsg := "Copying file 10" - if useXtrabackup { + if currentSetupType == XtraBackup { stopRestoreMsg = "Restore: Preparing" - useXtrabackup = false - defer func() { - useXtrabackup = true - }() } args := append([]string{"--server", localCluster.VtctlclientProcess.Server, "--alsologtostderr"}, "RestoreFromBackup", "--", primary.Alias) @@ -1330,9 +1345,9 @@ func TestReplicaRestoreToTimestamp(t *testing.T, restoreToTimestamp time.Time, e } func verifyTabletBackupStats(t *testing.T, vars map[string]any) { - // Currently only the builtin backup engine instruments bytes-processed - // counts. - if !useXtrabackup { + switch currentSetupType { + // Currently only the builtin backup engine instruments bytes-processed counts. + case BuiltinBackup: require.Contains(t, vars, "BackupBytes") bb := vars["BackupBytes"].(map[string]any) require.Contains(t, bb, "BackupEngine.Builtin.Compressor:Write") @@ -1346,8 +1361,10 @@ func verifyTabletBackupStats(t *testing.T, vars map[string]any) { require.Contains(t, vars, "BackupCount") bc := vars["BackupCount"].(map[string]any) require.Contains(t, bc, "-.-.Backup") - // Currently only the builtin backup engine implements operation counts. - if !useXtrabackup { + + switch currentSetupType { + // Currently only the builtin backup engine instruments bytes-processed counts. + case BuiltinBackup: require.Contains(t, bc, "BackupEngine.Builtin.Compressor:Close") require.Contains(t, bc, "BackupEngine.Builtin.Destination:Close") require.Contains(t, bc, "BackupEngine.Builtin.Destination:Open") @@ -1358,8 +1375,10 @@ func verifyTabletBackupStats(t *testing.T, vars map[string]any) { require.Contains(t, vars, "BackupDurationNanoseconds") bd := vars["BackupDurationNanoseconds"] require.Contains(t, bd, "-.-.Backup") + + switch currentSetupType { // Currently only the builtin backup engine emits timings. - if !useXtrabackup { + case BuiltinBackup: require.Contains(t, bd, "BackupEngine.Builtin.Compressor:Close") require.Contains(t, bd, "BackupEngine.Builtin.Compressor:Write") require.Contains(t, bd, "BackupEngine.Builtin.Destination:Close") @@ -1369,6 +1388,7 @@ func verifyTabletBackupStats(t *testing.T, vars map[string]any) { require.Contains(t, bd, "BackupEngine.Builtin.Source:Open") require.Contains(t, bd, "BackupEngine.Builtin.Source:Read") } + if backupstorage.BackupStorageImplementation == "file" { require.Contains(t, bd, "BackupStorage.File.File:Write") } @@ -1393,7 +1413,8 @@ func verifyTabletRestoreStats(t *testing.T, vars map[string]any) { verifyRestorePositionAndTimeStats(t, vars) - if !useXtrabackup { + switch currentSetupType { + case BuiltinBackup: require.Contains(t, vars, "RestoreBytes") bb := vars["RestoreBytes"].(map[string]any) require.Contains(t, bb, "BackupEngine.Builtin.Decompressor:Read") @@ -1405,8 +1426,10 @@ func verifyTabletRestoreStats(t *testing.T, vars map[string]any) { require.Contains(t, vars, "RestoreCount") bc := vars["RestoreCount"].(map[string]any) require.Contains(t, bc, "-.-.Restore") + + switch currentSetupType { // Currently only the builtin backup engine emits operation counts. - if !useXtrabackup { + case BuiltinBackup: require.Contains(t, bc, "BackupEngine.Builtin.Decompressor:Close") require.Contains(t, bc, "BackupEngine.Builtin.Destination:Close") require.Contains(t, bc, "BackupEngine.Builtin.Destination:Open") @@ -1417,8 +1440,10 @@ func verifyTabletRestoreStats(t *testing.T, vars map[string]any) { require.Contains(t, vars, "RestoreDurationNanoseconds") bd := vars["RestoreDurationNanoseconds"] require.Contains(t, bd, "-.-.Restore") + + switch currentSetupType { // Currently only the builtin backup engine emits timings. - if !useXtrabackup { + case BuiltinBackup: require.Contains(t, bd, "BackupEngine.Builtin.Decompressor:Close") require.Contains(t, bd, "BackupEngine.Builtin.Decompressor:Read") require.Contains(t, bd, "BackupEngine.Builtin.Destination:Close") @@ -1428,5 +1453,6 @@ func verifyTabletRestoreStats(t *testing.T, vars map[string]any) { require.Contains(t, bd, "BackupEngine.Builtin.Source:Open") require.Contains(t, bd, "BackupEngine.Builtin.Source:Read") } + require.Contains(t, bd, "BackupStorage.File.File:Read") } diff --git a/go/test/endtoend/vreplication/testdata/config/init_testserver_db.sql b/go/test/endtoend/vreplication/testdata/config/init_testserver_db.sql index 03df754ea21..c68cc0a1854 100644 --- a/go/test/endtoend/vreplication/testdata/config/init_testserver_db.sql +++ b/go/test/endtoend/vreplication/testdata/config/init_testserver_db.sql @@ -41,6 +41,7 @@ DROP DATABASE IF EXISTS test; CREATE USER 'vt_dba'@'localhost'; GRANT ALL ON *.* TO 'vt_dba'@'localhost'; GRANT GRANT OPTION ON *.* TO 'vt_dba'@'localhost'; +GRANT PROXY ON ''@'' TO 'vt_dba'@'localhost' WITH GRANT OPTION; # User for app traffic, with global read-write access. CREATE USER 'vt_app'@'localhost'; diff --git a/go/vt/mysqlctl/backup.go b/go/vt/mysqlctl/backup.go index fb401966c50..a90ab2f7f52 100644 --- a/go/vt/mysqlctl/backup.go +++ b/go/vt/mysqlctl/backup.go @@ -17,9 +17,11 @@ limitations under the License. package mysqlctl import ( + "bufio" "context" "errors" "fmt" + "io" "os" "path/filepath" "strings" @@ -29,6 +31,7 @@ import ( "vitess.io/vitess/go/textutil" "vitess.io/vitess/go/vt/log" + "vitess.io/vitess/go/vt/logutil" "vitess.io/vitess/go/vt/mysqlctl/backupstats" "vitess.io/vitess/go/vt/mysqlctl/backupstorage" "vitess.io/vitess/go/vt/proto/vtrpc" @@ -440,17 +443,19 @@ func Restore(ctx context.Context, params RestoreParams) (*BackupManifest, error) return nil, err } - // mysqld needs to be running in order for mysql_upgrade to work. - // If we've just restored from a backup from previous MySQL version then mysqld - // may fail to start due to a different structure of mysql.* tables. The flag - // --skip-grant-tables ensures that these tables are not read until mysql_upgrade - // is executed. And since with --skip-grant-tables anyone can connect to MySQL - // without password, we are passing --skip-networking to greatly reduce the set - // of those who can connect. - params.Logger.Infof("Restore: starting mysqld for mysql_upgrade") - // Note Start will use dba user for waiting, this is fine, it will be allowed. - if err := params.Mysqld.Start(context.Background(), params.Cnf, "--skip-grant-tables", "--skip-networking"); err != nil { - return nil, err + if re.ShouldStartMySQLAfterRestore() { // all engines except mysqlshell since MySQL is always running there + // mysqld needs to be running in order for mysql_upgrade to work. + // If we've just restored from a backup from previous MySQL version then mysqld + // may fail to start due to a different structure of mysql.* tables. The flag + // --skip-grant-tables ensures that these tables are not read until mysql_upgrade + // is executed. And since with --skip-grant-tables anyone can connect to MySQL + // without password, we are passing --skip-networking to greatly reduce the set + // of those who can connect. + params.Logger.Infof("Restore: starting mysqld for mysql_upgrade") + // Note Start will use dba user for waiting, this is fine, it will be allowed. + if err := params.Mysqld.Start(context.Background(), params.Cnf, "--skip-grant-tables", "--skip-networking"); err != nil { + return nil, err + } } params.Logger.Infof("Restore: running mysql_upgrade") @@ -496,3 +501,20 @@ func Restore(ctx context.Context, params RestoreParams) (*BackupManifest, error) params.Logger.Infof("Restore: complete") return manifest, nil } + +// scanLinesToLogger scans full lines from the given Reader and sends them to +// the given Logger until EOF. +func scanLinesToLogger(prefix string, reader io.Reader, logger logutil.Logger, doneFunc func()) { + defer doneFunc() + + scanner := bufio.NewScanner(reader) + for scanner.Scan() { + line := scanner.Text() + logger.Infof("%s: %s", prefix, line) + } + if err := scanner.Err(); err != nil { + // This is usually run in a background goroutine, so there's no point + // returning an error. Just log it. + logger.Warningf("error scanning lines from %s: %v", prefix, err) + } +} diff --git a/go/vt/mysqlctl/backup_test.go b/go/vt/mysqlctl/backup_test.go index ad7e0faab98..83defd0e170 100644 --- a/go/vt/mysqlctl/backup_test.go +++ b/go/vt/mysqlctl/backup_test.go @@ -26,9 +26,11 @@ import ( "path" "reflect" "sort" + "sync" "testing" "time" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "vitess.io/vitess/go/test/utils" @@ -37,6 +39,7 @@ import ( "vitess.io/vitess/go/mysql" "vitess.io/vitess/go/mysql/fakesqldb" + "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/logutil" "vitess.io/vitess/go/vt/mysqlctl/backupstats" "vitess.io/vitess/go/vt/mysqlctl/backupstorage" @@ -678,3 +681,73 @@ func (fbe *fakeBackupRestoreEnv) setStats(stats *backupstats.FakeStats) { fbe.restoreParams.Stats = nil fbe.stats = nil } + +func TestParseBackupName(t *testing.T) { + // backup name doesn't contain 3 parts + _, _, err := ParseBackupName("dir", "asd.saddsa") + assert.ErrorContains(t, err, "cannot backup name") + + // Invalid time + bt, al, err := ParseBackupName("dir", "2024-03-18.123.tablet_id") + assert.Nil(t, bt) + assert.Nil(t, al) + assert.NoError(t, err) + + // Valid case + bt, al, err = ParseBackupName("dir", "2024-03-18.180911.cell1-42") + assert.NotNil(t, *bt, time.Date(2024, 03, 18, 18, 9, 11, 0, time.UTC)) + assert.Equal(t, "cell1", al.Cell) + assert.Equal(t, uint32(42), al.Uid) + assert.NoError(t, err) +} + +func TestShouldRestore(t *testing.T) { + env := createFakeBackupRestoreEnv(t) + + b, err := ShouldRestore(env.ctx, env.restoreParams) + assert.False(t, b) + assert.Error(t, err) + + env.restoreParams.DeleteBeforeRestore = true + b, err = ShouldRestore(env.ctx, env.restoreParams) + assert.True(t, b) + assert.NoError(t, err) + env.restoreParams.DeleteBeforeRestore = false + + env.mysqld.FetchSuperQueryMap = map[string]*sqltypes.Result{ + "SHOW DATABASES": {Rows: [][]sqltypes.Value{{sqltypes.NewVarBinary("any_db")}}}, + } + b, err = ShouldRestore(env.ctx, env.restoreParams) + assert.NoError(t, err) + assert.True(t, b) + + env.mysqld.FetchSuperQueryMap = map[string]*sqltypes.Result{ + "SHOW DATABASES": {Rows: [][]sqltypes.Value{{sqltypes.NewVarBinary("test")}}}, + } + b, err = ShouldRestore(env.ctx, env.restoreParams) + assert.False(t, b) + assert.NoError(t, err) +} + +func TestScanLinesToLogger(t *testing.T) { + reader, writer := io.Pipe() + logger := logutil.NewMemoryLogger() + var wg sync.WaitGroup + + wg.Add(1) + go scanLinesToLogger("test", reader, logger, wg.Done) + + for i := range 100 { + _, err := writer.Write([]byte(fmt.Sprintf("foobar %d\n", i))) + require.NoError(t, err) + } + + writer.Close() + wg.Wait() + + require.Equal(t, 100, len(logger.Events)) + + for i, event := range logger.Events { + require.Equal(t, fmt.Sprintf("test: foobar %d", i), event.Value) + } +} diff --git a/go/vt/mysqlctl/backupengine.go b/go/vt/mysqlctl/backupengine.go index 915b5e6894f..6064e2d3e07 100644 --- a/go/vt/mysqlctl/backupengine.go +++ b/go/vt/mysqlctl/backupengine.go @@ -179,6 +179,7 @@ func (p *RestoreParams) IsIncrementalRecovery() bool { // Returns the manifest of a backup if successful, otherwise returns an error type RestoreEngine interface { ExecuteRestore(ctx context.Context, params RestoreParams, bh backupstorage.BackupHandle) (*BackupManifest, error) + ShouldStartMySQLAfterRestore() bool } // BackupRestoreEngine is a combination of BackupEngine and RestoreEngine. diff --git a/go/vt/mysqlctl/builtinbackupengine.go b/go/vt/mysqlctl/builtinbackupengine.go index af6b8fdbd85..e21325974e4 100644 --- a/go/vt/mysqlctl/builtinbackupengine.go +++ b/go/vt/mysqlctl/builtinbackupengine.go @@ -1215,6 +1215,11 @@ func (be *BuiltinBackupEngine) ShouldDrainForBackup(req *tabletmanagerdatapb.Bac return true } +// ShouldStartMySQLAfterRestore signifies if this backup engine needs to restart MySQL once the restore is completed. +func (be *BuiltinBackupEngine) ShouldStartMySQLAfterRestore() bool { + return true +} + func getPrimaryPosition(ctx context.Context, tmc tmclient.TabletManagerClient, ts *topo.Server, keyspace, shard string) (replication.Position, error) { si, err := ts.GetShard(ctx, keyspace, shard) if err != nil { diff --git a/go/vt/mysqlctl/fakebackupengine.go b/go/vt/mysqlctl/fakebackupengine.go index d78282e6aff..180922347e1 100644 --- a/go/vt/mysqlctl/fakebackupengine.go +++ b/go/vt/mysqlctl/fakebackupengine.go @@ -91,3 +91,7 @@ func (be *FakeBackupEngine) ShouldDrainForBackup(req *tabletmanagerdatapb.Backup be.ShouldDrainForBackupCalls = be.ShouldDrainForBackupCalls + 1 return be.ShouldDrainForBackupReturn } + +func (be *FakeBackupEngine) ShouldStartMySQLAfterRestore() bool { + return true +} diff --git a/go/vt/mysqlctl/fakemysqldaemon.go b/go/vt/mysqlctl/fakemysqldaemon.go index 3f2eb741d92..788432884eb 100644 --- a/go/vt/mysqlctl/fakemysqldaemon.go +++ b/go/vt/mysqlctl/fakemysqldaemon.go @@ -18,6 +18,7 @@ package mysqlctl import ( "context" + "errors" "fmt" "reflect" "regexp" @@ -530,6 +531,11 @@ func (fmd *FakeMysqlDaemon) Promote(hookExtraEnv map[string]string) (replication return fmd.PromoteResult, nil } +// ExecuteSuperQuery is part of the MysqlDaemon interface +func (fmd *FakeMysqlDaemon) ExecuteSuperQuery(ctx context.Context, query string) error { + return fmd.ExecuteSuperQueryList(ctx, []string{query}) +} + // ExecuteSuperQueryList is part of the MysqlDaemon interface func (fmd *FakeMysqlDaemon) ExecuteSuperQueryList(ctx context.Context, queryList []string) error { for _, query := range queryList { @@ -722,3 +728,13 @@ func (fmd *FakeMysqlDaemon) GetVersionString(ctx context.Context) (string, error func (fmd *FakeMysqlDaemon) GetVersionComment(ctx context.Context) (string, error) { return "", nil } + +// AcquireGlobalReadLock is part of the MysqlDaemon interface. +func (fmd *FakeMysqlDaemon) AcquireGlobalReadLock(ctx context.Context) error { + return errors.New("not implemented") +} + +// ReleaseGlobalReadLock is part of the MysqlDaemon interface. +func (fmd *FakeMysqlDaemon) ReleaseGlobalReadLock(ctx context.Context) error { + return errors.New("not implemented") +} diff --git a/go/vt/mysqlctl/mysql_daemon.go b/go/vt/mysqlctl/mysql_daemon.go index 9e8baebefd6..13c0444fd4d 100644 --- a/go/vt/mysqlctl/mysql_daemon.go +++ b/go/vt/mysqlctl/mysql_daemon.go @@ -110,12 +110,22 @@ type MysqlDaemon interface { // GetVersionComment returns the version comment GetVersionComment(ctx context.Context) (string, error) + // ExecuteSuperQuery executes a single query, no result + ExecuteSuperQuery(ctx context.Context, query string) error + // ExecuteSuperQueryList executes a list of queries, no result ExecuteSuperQueryList(ctx context.Context, queryList []string) error // FetchSuperQuery executes one query, returns the result FetchSuperQuery(ctx context.Context, query string) (*sqltypes.Result, error) + // AcquireGlobalReadLock acquires a global read lock and keeps the connection so + // as to release it with the function below. + AcquireGlobalReadLock(ctx context.Context) error + + // ReleaseGlobalReadLock release a lock acquired with the connection from the above function. + ReleaseGlobalReadLock(ctx context.Context) error + // Close will close this instance of Mysqld. It will wait for all dba // queries to be finished. Close() diff --git a/go/vt/mysqlctl/mysqld.go b/go/vt/mysqlctl/mysqld.go index f19cc681df7..34f5d155ed2 100644 --- a/go/vt/mysqlctl/mysqld.go +++ b/go/vt/mysqlctl/mysqld.go @@ -106,9 +106,10 @@ var ( // Mysqld is the object that represents a mysqld daemon running on this server. type Mysqld struct { - dbcfgs *dbconfigs.DBConfigs - dbaPool *dbconnpool.ConnectionPool - appPool *dbconnpool.ConnectionPool + dbcfgs *dbconfigs.DBConfigs + dbaPool *dbconnpool.ConnectionPool + appPool *dbconnpool.ConnectionPool + lockConn *dbconnpool.PooledDBConnection capabilities capabilitySet diff --git a/go/vt/mysqlctl/mysqlshellbackupengine.go b/go/vt/mysqlctl/mysqlshellbackupengine.go new file mode 100644 index 00000000000..38dccc8c622 --- /dev/null +++ b/go/vt/mysqlctl/mysqlshellbackupengine.go @@ -0,0 +1,582 @@ +/* +Copyright 2024 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package mysqlctl + +import ( + "bufio" + "context" + "encoding/json" + "errors" + "fmt" + "io" + "os" + "os/exec" + "path" + "slices" + "strings" + "sync" + "time" + + "github.com/spf13/pflag" + + "vitess.io/vitess/go/mysql" + "vitess.io/vitess/go/mysql/capabilities" + "vitess.io/vitess/go/vt/log" + "vitess.io/vitess/go/vt/mysqlctl/backupstorage" + tabletmanagerdatapb "vitess.io/vitess/go/vt/proto/tabletmanagerdata" + "vitess.io/vitess/go/vt/servenv" + "vitess.io/vitess/go/vt/vterrors" +) + +var ( + // location to store the mysql shell backup + mysqlShellBackupLocation = "" + // flags passed to the mysql shell utility, used both on dump/restore + mysqlShellFlags = "--defaults-file=/dev/null --js -h localhost" + // flags passed to the Dump command, as a JSON string + mysqlShellDumpFlags = `{"threads": 4}` + // flags passed to the Load command, as a JSON string + mysqlShellLoadFlags = `{"threads": 4, "loadUsers": true, "updateGtidSet": "replace", "skipBinlog": true, "progressFile": ""}` + // drain a tablet when taking a backup + mysqlShellBackupShouldDrain = false + // disable redo logging and double write buffer + mysqlShellSpeedUpRestore = false + + // use when checking if we need to create the directory on the local filesystem or not. + knownObjectStoreParams = []string{"s3BucketName", "osBucketName", "azureContainerName"} + + MySQLShellPreCheckError = errors.New("MySQLShellPreCheckError") + + // internal databases not backed up by MySQL Shell + internalDBs = []string{ + "information_schema", "mysql", "ndbinfo", "performance_schema", "sys", + } + // reserved MySQL users https://dev.mysql.com/doc/refman/8.0/en/reserved-accounts.html + reservedUsers = []string{ + "mysql.sys@localhost", "mysql.session@localhost", "mysql.infoschema@localhost", + } +) + +// MySQLShellBackupManifest represents a backup. +type MySQLShellBackupManifest struct { + // BackupManifest is an anonymous embedding of the base manifest struct. + // Note that the manifest itself doesn't fill the Position field, as we have + // no way of fetching that information from mysqlsh at the moment. + BackupManifest + + // Location of the backup directory + BackupLocation string + // Params are the parameters that backup was created with + Params string +} + +func init() { + BackupRestoreEngineMap[mysqlShellBackupEngineName] = &MySQLShellBackupEngine{} + + for _, cmd := range []string{"vtcombo", "vttablet", "vtbackup", "vttestserver", "vtctldclient"} { + servenv.OnParseFor(cmd, registerMysqlShellBackupEngineFlags) + } +} + +func registerMysqlShellBackupEngineFlags(fs *pflag.FlagSet) { + fs.StringVar(&mysqlShellBackupLocation, "mysql-shell-backup-location", mysqlShellBackupLocation, "location where the backup will be stored") + fs.StringVar(&mysqlShellFlags, "mysql-shell-flags", mysqlShellFlags, "execution flags to pass to mysqlsh binary to be used during dump/load") + fs.StringVar(&mysqlShellDumpFlags, "mysql-shell-dump-flags", mysqlShellDumpFlags, "flags to pass to mysql shell dump utility. This should be a JSON string and will be saved in the MANIFEST") + fs.StringVar(&mysqlShellLoadFlags, "mysql-shell-load-flags", mysqlShellLoadFlags, "flags to pass to mysql shell load utility. This should be a JSON string") + fs.BoolVar(&mysqlShellBackupShouldDrain, "mysql-shell-should-drain", mysqlShellBackupShouldDrain, "decide if we should drain while taking a backup or continue to serving traffic") + fs.BoolVar(&mysqlShellSpeedUpRestore, "mysql-shell-speedup-restore", mysqlShellSpeedUpRestore, "speed up restore by disabling redo logging and double write buffer during the restore process") +} + +// MySQLShellBackupEngine encapsulates the logic to implement the restoration +// of a mysql-shell based backup. +type MySQLShellBackupEngine struct { +} + +const ( + mysqlShellBackupBinaryName = "mysqlsh" + mysqlShellBackupEngineName = "mysqlshell" +) + +func (be *MySQLShellBackupEngine) ExecuteBackup(ctx context.Context, params BackupParams, bh backupstorage.BackupHandle) (result BackupResult, finalErr error) { + params.Logger.Infof("Starting ExecuteBackup in %s", params.TabletAlias) + + location := path.Join(mysqlShellBackupLocation, bh.Directory(), bh.Name()) + + err := be.backupPreCheck(location) + if err != nil { + return BackupUnusable, vterrors.Wrap(err, "failed backup precheck") + } + + serverUUID, err := params.Mysqld.GetServerUUID(ctx) + if err != nil { + return BackupUnusable, vterrors.Wrap(err, "can't get server uuid") + } + + mysqlVersion, err := params.Mysqld.GetVersionString(ctx) + if err != nil { + return BackupUnusable, vterrors.Wrap(err, "can't get MySQL version") + } + + args := []string{} + if mysqlShellFlags != "" { + args = append(args, strings.Fields(mysqlShellFlags)...) + } + + args = append(args, "-e", fmt.Sprintf("util.dumpInstance(%q, %s)", + location, + mysqlShellDumpFlags, + )) + + // to be able to get the consistent GTID sets, we will acquire a global read lock before starting mysql shell. + // oncce we have the lock, we start it and wait unti it has acquired and release its global read lock, which + // should guarantee that both use and mysql shell are seeing the same executed GTID sets. + // after this we release the lock so that replication can continue. this usually should take just a few seconds. + params.Logger.Infof("acquiring a global read lock before fetching the executed GTID sets") + err = params.Mysqld.AcquireGlobalReadLock(ctx) + if err != nil { + return BackupUnusable, vterrors.Wrap(err, "failed to acquire read lock to start backup") + } + lockAcquired := time.Now() // we will report how long we hold the lock for + + posBeforeBackup, err := params.Mysqld.PrimaryPosition(ctx) + if err != nil { + return BackupUnusable, vterrors.Wrap(err, "failed to fetch position") + } + + cmd := exec.CommandContext(ctx, mysqlShellBackupBinaryName, args...) + + params.Logger.Infof("running %s", cmd.String()) + + cmdOut, err := cmd.StdoutPipe() + if err != nil { + return BackupUnusable, vterrors.Wrap(err, "cannot create stdout pipe") + } + cmdOriginalErr, err := cmd.StderrPipe() + if err != nil { + return BackupUnusable, vterrors.Wrap(err, "cannot create stderr pipe") + } + if err := cmd.Start(); err != nil { + return BackupUnusable, vterrors.Wrap(err, "can't start mysqlshell") + } + + pipeReader, pipeWriter := io.Pipe() + cmdErr := io.TeeReader(cmdOriginalErr, pipeWriter) + + cmdWg := &sync.WaitGroup{} + cmdWg.Add(3) + go releaseReadLock(ctx, pipeReader, params, cmdWg, lockAcquired) + go scanLinesToLogger(mysqlShellBackupEngineName+" stdout", cmdOut, params.Logger, cmdWg.Done) + go scanLinesToLogger(mysqlShellBackupEngineName+" stderr", cmdErr, params.Logger, cmdWg.Done) + + // Get exit status. + if err := cmd.Wait(); err != nil { + return BackupUnusable, vterrors.Wrap(err, mysqlShellBackupEngineName+" failed") + } + + // close the pipeWriter and wait for the goroutines to have read all the logs + pipeWriter.Close() + cmdWg.Wait() + + // open the MANIFEST + params.Logger.Infof("Writing backup MANIFEST") + mwc, err := bh.AddFile(ctx, backupManifestFileName, backupstorage.FileSizeUnknown) + if err != nil { + return BackupUnusable, vterrors.Wrapf(err, "cannot add %v to backup", backupManifestFileName) + } + defer closeFile(mwc, backupManifestFileName, params.Logger, &finalErr) + + // JSON-encode and write the MANIFEST + bm := &MySQLShellBackupManifest{ + // Common base fields + BackupManifest: BackupManifest{ + BackupMethod: mysqlShellBackupEngineName, + // the position is empty here because we have no way of capturing it from mysqlsh + // we will capture it when doing the restore as mysqlsh can replace the GTIDs with + // what it has stored in the backup. + Position: posBeforeBackup, + PurgedPosition: posBeforeBackup, + BackupTime: FormatRFC3339(params.BackupTime.UTC()), + FinishedTime: FormatRFC3339(time.Now().UTC()), + ServerUUID: serverUUID, + TabletAlias: params.TabletAlias, + Keyspace: params.Keyspace, + Shard: params.Shard, + MySQLVersion: mysqlVersion, + UpgradeSafe: true, + }, + + // mysql shell backup specific fields + BackupLocation: location, + Params: mysqlShellLoadFlags, + } + + data, err := json.MarshalIndent(bm, "", " ") + if err != nil { + return BackupUnusable, vterrors.Wrapf(err, "cannot JSON encode %v", backupManifestFileName) + } + if _, err := mwc.Write([]byte(data)); err != nil { + return BackupUnusable, vterrors.Wrapf(err, "cannot write %v", backupManifestFileName) + } + + params.Logger.Infof("Backup completed") + return BackupUsable, nil +} + +func (be *MySQLShellBackupEngine) ExecuteRestore(ctx context.Context, params RestoreParams, bh backupstorage.BackupHandle) (*BackupManifest, error) { + params.Logger.Infof("Calling ExecuteRestore for %s (DeleteBeforeRestore: %v)", params.DbName, params.DeleteBeforeRestore) + + shouldDeleteUsers, err := be.restorePreCheck(ctx, params) + if err != nil { + return nil, vterrors.Wrap(err, "failed restore precheck") + } + + var bm MySQLShellBackupManifest + if err := getBackupManifestInto(ctx, bh, &bm); err != nil { + return nil, err + } + + // mark restore as in progress + if err := createStateFile(params.Cnf); err != nil { + return nil, err + } + + // make sure semi-sync is disabled, otherwise we will wait forever for acknowledgements + err = params.Mysqld.SetSemiSyncEnabled(ctx, false, false) + if err != nil { + return nil, vterrors.Wrap(err, "disable semi-sync failed") + } + + params.Logger.Infof("restoring on an existing tablet, so dropping database %q", params.DbName) + + readonly, err := params.Mysqld.IsSuperReadOnly(ctx) + if err != nil { + return nil, vterrors.Wrap(err, fmt.Sprintf("checking if mysqld has super_read_only=enable: %v", err)) + } + + if readonly { + resetFunc, err := params.Mysqld.SetSuperReadOnly(ctx, false) + if err != nil { + return nil, vterrors.Wrap(err, fmt.Sprintf("unable to disable super-read-only: %v", err)) + } + + defer func() { + err := resetFunc() + if err != nil { + params.Logger.Errorf("Not able to set super_read_only to its original value after restore") + } + }() + } + + err = cleanupMySQL(ctx, params, shouldDeleteUsers) + if err != nil { + log.Errorf(err.Error()) + // time.Sleep(time.Minute * 2) + return nil, vterrors.Wrap(err, "error cleaning MySQL") + } + + // we need to get rid of all the current replication information on the host. + err = params.Mysqld.ResetReplication(ctx) + if err != nil { + return nil, vterrors.Wrap(err, "unable to reset replication") + } + + // this is required so we can load the backup generated by MySQL Shell. we will disable it afterwards. + err = params.Mysqld.ExecuteSuperQuery(ctx, "SET GLOBAL LOCAL_INFILE=1") + if err != nil { + return nil, vterrors.Wrap(err, "unable to set local_infile=1") + } + + if mysqlShellSpeedUpRestore { + // disable redo logging and double write buffer if we are configured to do so. + err = params.Mysqld.ExecuteSuperQuery(ctx, "ALTER INSTANCE DISABLE INNODB REDO_LOG") + if err != nil { + return nil, vterrors.Wrap(err, "unable to disable REDO_LOG") + } + params.Logger.Infof("Disabled REDO_LOG") + + defer func() { // re-enable once we are done with the restore. + err := params.Mysqld.ExecuteSuperQuery(ctx, "ALTER INSTANCE ENABLE INNODB REDO_LOG") + if err != nil { + params.Logger.Errorf("unable to re-enable REDO_LOG: %v", err) + } else { + params.Logger.Infof("Disabled REDO_LOG") + } + }() + } + + // we need to disable SuperReadOnly otherwise we won't be able to restore the backup properly. + // once the backups is complete, we will restore it to its previous state. + resetFunc, err := be.handleSuperReadOnly(ctx, params) + if err != nil { + return nil, vterrors.Wrap(err, "unable to disable super-read-only") + } + defer resetFunc() + + args := []string{} + + if mysqlShellFlags != "" { + args = append(args, strings.Fields(mysqlShellFlags)...) + } + + args = append(args, "-e", fmt.Sprintf("util.loadDump(%q, %s)", + bm.BackupLocation, + mysqlShellLoadFlags, + )) + + cmd := exec.CommandContext(ctx, "mysqlsh", args...) + + params.Logger.Infof("running %s", cmd.String()) + + cmdOut, err := cmd.StdoutPipe() + if err != nil { + return nil, vterrors.Wrap(err, "cannot create stdout pipe") + } + cmdErr, err := cmd.StderrPipe() + if err != nil { + return nil, vterrors.Wrap(err, "cannot create stderr pipe") + } + if err := cmd.Start(); err != nil { + return nil, vterrors.Wrap(err, "can't start xbstream") + } + + cmdWg := &sync.WaitGroup{} + cmdWg.Add(2) + go scanLinesToLogger(mysqlShellBackupEngineName+" stdout", cmdOut, params.Logger, cmdWg.Done) + go scanLinesToLogger(mysqlShellBackupEngineName+" stderr", cmdErr, params.Logger, cmdWg.Done) + cmdWg.Wait() + + // Get the exit status. + if err := cmd.Wait(); err != nil { + return nil, vterrors.Wrap(err, mysqlShellBackupEngineName+" failed") + } + params.Logger.Infof("%s completed successfully", mysqlShellBackupBinaryName) + + // disable local_infile now that the restore is done. + err = params.Mysqld.ExecuteSuperQuery(ctx, "SET GLOBAL LOCAL_INFILE=0") + if err != nil { + return nil, vterrors.Wrap(err, "unable to set local_infile=0") + } + params.Logger.Infof("set local_infile=0") + + params.Logger.Infof("Restore completed") + + return &bm.BackupManifest, nil +} + +// ShouldDrainForBackup satisfies the BackupEngine interface +// MySQL Shell backups can be taken while MySQL is running so we can control this via a flag. +func (be *MySQLShellBackupEngine) ShouldDrainForBackup(req *tabletmanagerdatapb.BackupRequest) bool { + return mysqlShellBackupShouldDrain +} + +// ShouldStartMySQLAfterRestore signifies if this backup engine needs to restart MySQL once the restore is completed. +// Since MySQL Shell operates on a live MySQL instance, there is no need to start it once the restore is completed +func (be *MySQLShellBackupEngine) ShouldStartMySQLAfterRestore() bool { + return false +} + +func (be *MySQLShellBackupEngine) backupPreCheck(location string) error { + if mysqlShellBackupLocation == "" { + return fmt.Errorf("%w: no backup location set via --mysql-shell-backup-location", MySQLShellPreCheckError) + } + + if mysqlShellFlags == "" || !strings.Contains(mysqlShellFlags, "--js") { + return fmt.Errorf("%w: at least the --js flag is required in the value of the flag --mysql-shell-flags", MySQLShellPreCheckError) + } + + // make sure the targe directory exists if the target location for the backup is not an object store + // (e.g. is the local filesystem) as MySQL Shell doesn't create the entire path beforehand: + isObjectStorage := false + for _, objStore := range knownObjectStoreParams { + if strings.Contains(mysqlShellDumpFlags, objStore) { + isObjectStorage = true + break + } + } + + if !isObjectStorage { + err := os.MkdirAll(location, 0o750) + if err != nil { + return fmt.Errorf("failure creating directory %s: %w", location, err) + } + } + + return nil +} + +func (be *MySQLShellBackupEngine) restorePreCheck(ctx context.Context, params RestoreParams) (shouldDeleteUsers bool, err error) { + if mysqlShellFlags == "" { + return shouldDeleteUsers, fmt.Errorf("%w: at least the --js flag is required in the value of the flag --mysql-shell-flags", MySQLShellPreCheckError) + } + + loadFlags := map[string]interface{}{} + err = json.Unmarshal([]byte(mysqlShellLoadFlags), &loadFlags) + if err != nil { + return false, fmt.Errorf("%w: unable to parse JSON of load flags", MySQLShellPreCheckError) + } + + if val, ok := loadFlags["updateGtidSet"]; !ok || val != "replace" { + return false, fmt.Errorf("%w: mysql-shell needs to restore with updateGtidSet set to \"replace\" to work with Vitess", MySQLShellPreCheckError) + } + + if val, ok := loadFlags["progressFile"]; !ok || val != "" { + return false, fmt.Errorf("%w: \"progressFile\" needs to be empty as vitess always starts a restore from scratch", MySQLShellPreCheckError) + } + + if val, ok := loadFlags["skipBinlog"]; !ok || val != true { + return false, fmt.Errorf("%w: \"skipBinlog\" needs to set to true", MySQLShellPreCheckError) + } + + if val, ok := loadFlags["loadUsers"]; ok && val == true { + shouldDeleteUsers = true + } + + if mysqlShellSpeedUpRestore { + version, err := params.Mysqld.GetVersionString(ctx) + if err != nil { + return false, fmt.Errorf("%w: failed to fetch MySQL version: %v", MySQLShellPreCheckError, err) + } + + capableOf := mysql.ServerVersionCapableOf(version) + capable, err := capableOf(capabilities.DisableRedoLogFlavorCapability) + if err != nil { + return false, fmt.Errorf("%w: error checking if server supports disabling redo log: %v", MySQLShellPreCheckError, err) + } + + if !capable { + return false, fmt.Errorf("%w: MySQL version doesn't support disabling the redo log (must be >=8.0.21)", MySQLShellPreCheckError) + } + } + + return shouldDeleteUsers, nil +} + +func (be *MySQLShellBackupEngine) handleSuperReadOnly(ctx context.Context, params RestoreParams) (func(), error) { + readonly, err := params.Mysqld.IsSuperReadOnly(ctx) + if err != nil { + return nil, vterrors.Wrap(err, fmt.Sprintf("checking if mysqld has super_read_only=enable: %v", err)) + } + + params.Logger.Infof("Is Super Read Only: %v", readonly) + + if readonly { + resetFunc, err := params.Mysqld.SetSuperReadOnly(ctx, false) + if err != nil { + return nil, vterrors.Wrap(err, fmt.Sprintf("unable to disable super-read-only: %v", err)) + } + + return func() { + err := resetFunc() + if err != nil { + params.Logger.Errorf("Not able to set super_read_only to its original value after restore") + } + }, nil + } + + return func() {}, nil +} + +// releaseReadLock will keep reading the MySQL Shell STDERR waiting until the point it has acquired its lock +func releaseReadLock(ctx context.Context, reader io.Reader, params BackupParams, wg *sync.WaitGroup, lockAcquired time.Time) { + defer wg.Done() + + scanner := bufio.NewScanner(reader) + released := false + for scanner.Scan() { + line := scanner.Text() + + if !released { + + if !strings.Contains(line, "Global read lock has been released") { + continue + } + released = true + + params.Logger.Infof("mysql shell released its global read lock, doing the same") + + err := params.Mysqld.ReleaseGlobalReadLock(ctx) + if err != nil { + params.Logger.Errorf("unable to release global read lock: %v", err) + } + + params.Logger.Infof("global read lock released after %v", time.Since(lockAcquired)) + } + } + if err := scanner.Err(); err != nil { + params.Logger.Errorf("error reading from reader: %v", err) + } +} + +func cleanupMySQL(ctx context.Context, params RestoreParams, shouldDeleteUsers bool) error { + params.Logger.Infof("Cleaning up MySQL ahead of a restore") + result, err := params.Mysqld.FetchSuperQuery(ctx, "SHOW DATABASES") + if err != nil { + return err + } + + // drop all databases + for _, row := range result.Rows { + dbName := row[0].ToString() + if slices.Contains(internalDBs, dbName) { + continue // not dropping internal DBs + } + + params.Logger.Infof("Dropping DB %q", dbName) + err = params.Mysqld.ExecuteSuperQuery(ctx, fmt.Sprintf("DROP DATABASE IF EXISTS `%s`", row[0].ToString())) + if err != nil { + return fmt.Errorf("error droppping database %q: %w", row[0].ToString(), err) + } + } + + if shouldDeleteUsers { + // get current user + var currentUser string + result, err = params.Mysqld.FetchSuperQuery(ctx, "SELECT user()") + if err != nil { + return fmt.Errorf("error fetching current user: %w", err) + } + + for _, row := range result.Rows { + currentUser = row[0].ToString() + } + + // drop all users except reserved ones + result, err = params.Mysqld.FetchSuperQuery(ctx, "SELECT user, host FROM mysql.user") + if err != nil { + return err + } + + for _, row := range result.Rows { + user := fmt.Sprintf("%s@%s", row[0].ToString(), row[1].ToString()) + + if user == currentUser { + continue // we don't drop the current user + } + if slices.Contains(reservedUsers, user) { + continue // we skip reserved MySQL users + } + + params.Logger.Infof("Dropping User %q", user) + err = params.Mysqld.ExecuteSuperQuery(ctx, fmt.Sprintf("DROP USER '%s'@'%s'", row[0].ToString(), row[1].ToString())) + if err != nil { + return fmt.Errorf("error droppping user %q: %w", user, err) + } + } + } + + return err +} diff --git a/go/vt/mysqlctl/mysqlshellbackupengine_test.go b/go/vt/mysqlctl/mysqlshellbackupengine_test.go new file mode 100644 index 00000000000..a0d0c8d7a1d --- /dev/null +++ b/go/vt/mysqlctl/mysqlshellbackupengine_test.go @@ -0,0 +1,309 @@ +/* +Copyright 2024 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package mysqlctl + +import ( + "context" + "fmt" + "path" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "vitess.io/vitess/go/mysql/fakesqldb" + "vitess.io/vitess/go/sqltypes" + "vitess.io/vitess/go/vt/logutil" + tabletmanagerdatapb "vitess.io/vitess/go/vt/proto/tabletmanagerdata" +) + +func TestMySQLShellBackupBackupPreCheck(t *testing.T) { + originalLocation := mysqlShellBackupLocation + originalFlags := mysqlShellFlags + defer func() { + mysqlShellBackupLocation = originalLocation + mysqlShellFlags = originalFlags + }() + + engine := MySQLShellBackupEngine{} + tests := []struct { + name string + location string + flags string + err error + }{ + { + "empty flags", + "", + `{}`, + MySQLShellPreCheckError, + }, + { + "only location", + "/dev/null", + "", + MySQLShellPreCheckError, + }, + { + "only flags", + "", + "--js", + MySQLShellPreCheckError, + }, + { + "both values present but without --js", + "", + "-h localhost", + MySQLShellPreCheckError, + }, + { + "supported values", + t.TempDir(), + "--js -h localhost", + nil, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + + mysqlShellBackupLocation = tt.location + mysqlShellFlags = tt.flags + assert.ErrorIs(t, engine.backupPreCheck(path.Join(mysqlShellBackupLocation, "test")), tt.err) + }) + } + +} + +func TestMySQLShellBackupRestorePreCheck(t *testing.T) { + original := mysqlShellLoadFlags + defer func() { mysqlShellLoadFlags = original }() + + engine := MySQLShellBackupEngine{} + tests := []struct { + name string + flags string + err error + shouldDeleteUsers bool + }{ + { + "empty load flags", + `{}`, + MySQLShellPreCheckError, + false, + }, + { + "only updateGtidSet", + `{"updateGtidSet": "replace"}`, + MySQLShellPreCheckError, + false, + }, + { + "only progressFile", + `{"progressFile": ""}`, + MySQLShellPreCheckError, + false, + }, + { + "both values but unsupported values", + `{"updateGtidSet": "append", "progressFile": "/tmp/test1"}`, + MySQLShellPreCheckError, + false, + }, + { + "supported values", + `{"updateGtidSet": "replace", "progressFile": "", "skipBinlog": true, "loadUsers": false}`, + nil, + false, + }, + { + "should delete users", + `{"updateGtidSet": "replace", "progressFile": "", "skipBinlog": true, "loadUsers": true}`, + nil, + true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mysqlShellLoadFlags = tt.flags + shouldDeleteUsers, err := engine.restorePreCheck(context.Background(), RestoreParams{}) + assert.ErrorIs(t, err, tt.err) + assert.Equal(t, tt.shouldDeleteUsers, shouldDeleteUsers) + }) + } + +} + +func TestMySQLShellBackupRestorePreCheckDisableRedolog(t *testing.T) { + original := mysqlShellSpeedUpRestore + defer func() { mysqlShellSpeedUpRestore = original }() + + mysqlShellSpeedUpRestore = true + engine := MySQLShellBackupEngine{} + + fakedb := fakesqldb.New(t) + defer fakedb.Close() + fakeMysqld := NewFakeMysqlDaemon(fakedb) // defaults to 8.0.32 + defer fakeMysqld.Close() + + params := RestoreParams{ + Mysqld: fakeMysqld, + } + + // this should work as it is supported since 8.0.21 + _, err := engine.restorePreCheck(context.Background(), params) + require.NoError(t, err, params) + + // it should error out if we change to an older version + fakeMysqld.Version = "8.0.20" + + _, err = engine.restorePreCheck(context.Background(), params) + require.ErrorIs(t, err, MySQLShellPreCheckError) + require.ErrorContains(t, err, "doesn't support disabling the redo log") +} + +func TestShouldDrainForBackupMySQLShell(t *testing.T) { + original := mysqlShellBackupShouldDrain + defer func() { mysqlShellBackupShouldDrain = original }() + + engine := MySQLShellBackupEngine{} + + mysqlShellBackupShouldDrain = false + + assert.False(t, engine.ShouldDrainForBackup(nil)) + assert.False(t, engine.ShouldDrainForBackup(&tabletmanagerdatapb.BackupRequest{})) + + mysqlShellBackupShouldDrain = true + + assert.True(t, engine.ShouldDrainForBackup(nil)) + assert.True(t, engine.ShouldDrainForBackup(&tabletmanagerdatapb.BackupRequest{})) +} + +func TestCleanupMySQL(t *testing.T) { + type userRecord struct { + user, host string + } + + tests := []struct { + name string + existingDBs []string + expectedDropDBs []string + currentUser string + existingUsers []userRecord + expectedDropUsers []string + shouldDeleteUsers bool + }{ + { + name: "testing only specific DBs", + existingDBs: []string{"_vt", "vt_test"}, + expectedDropDBs: []string{"_vt", "vt_test"}, + }, + { + name: "testing with internal dbs", + existingDBs: []string{"_vt", "mysql", "vt_test", "performance_schema"}, + expectedDropDBs: []string{"_vt", "vt_test"}, + }, + { + name: "with users but without delete", + existingDBs: []string{"_vt", "mysql", "vt_test", "performance_schema"}, + expectedDropDBs: []string{"_vt", "vt_test"}, + existingUsers: []userRecord{ + {"test", "localhost"}, + {"app", "10.0.0.1"}, + }, + expectedDropUsers: []string{}, + shouldDeleteUsers: false, + }, + { + name: "with users and delete", + existingDBs: []string{"_vt", "mysql", "vt_test", "performance_schema"}, + expectedDropDBs: []string{"_vt", "vt_test"}, + existingUsers: []userRecord{ + {"test", "localhost"}, + {"app", "10.0.0.1"}, + }, + expectedDropUsers: []string{"'test'@'localhost'", "'app'@'10.0.0.1'"}, + shouldDeleteUsers: true, + }, + { + name: "with reserved users", + existingDBs: []string{"_vt", "mysql", "vt_test", "performance_schema"}, + expectedDropDBs: []string{"_vt", "vt_test"}, + existingUsers: []userRecord{ + {"mysql.sys", "localhost"}, + {"mysql.infoschema", "localhost"}, + {"mysql.session", "localhost"}, + {"test", "localhost"}, + {"app", "10.0.0.1"}, + }, + expectedDropUsers: []string{"'test'@'localhost'", "'app'@'10.0.0.1'"}, + shouldDeleteUsers: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + fakedb := fakesqldb.New(t) + defer fakedb.Close() + mysql := NewFakeMysqlDaemon(fakedb) + defer mysql.Close() + + databases := [][]sqltypes.Value{} + for _, db := range tt.existingDBs { + databases = append(databases, []sqltypes.Value{sqltypes.NewVarChar(db)}) + } + + users := [][]sqltypes.Value{} + for _, record := range tt.existingUsers { + users = append(users, []sqltypes.Value{sqltypes.NewVarChar(record.user), sqltypes.NewVarChar(record.host)}) + } + + mysql.FetchSuperQueryMap = map[string]*sqltypes.Result{ + "SHOW DATABASES": {Rows: databases}, + "SELECT user()": {Rows: [][]sqltypes.Value{{sqltypes.NewVarChar(tt.currentUser)}}}, + "SELECT user, host FROM mysql.user": {Rows: users}, + } + + for _, drop := range tt.expectedDropDBs { + mysql.ExpectedExecuteSuperQueryList = append(mysql.ExpectedExecuteSuperQueryList, + fmt.Sprintf("DROP DATABASE IF EXISTS `%s`", drop), + ) + } + + if tt.shouldDeleteUsers { + for _, drop := range tt.expectedDropUsers { + mysql.ExpectedExecuteSuperQueryList = append(mysql.ExpectedExecuteSuperQueryList, + fmt.Sprintf("DROP USER %s", drop), + ) + } + } + + params := RestoreParams{ + Mysqld: mysql, + Logger: logutil.NewMemoryLogger(), + } + + err := cleanupMySQL(context.Background(), params, tt.shouldDeleteUsers) + require.NoError(t, err) + + require.Equal(t, len(tt.expectedDropDBs)+len(tt.expectedDropUsers), mysql.ExpectedExecuteSuperQueryCurrent, + "unexpected number of queries executed") + }) + } + +} diff --git a/go/vt/mysqlctl/query.go b/go/vt/mysqlctl/query.go index 5e21913c617..cde877c3d47 100644 --- a/go/vt/mysqlctl/query.go +++ b/go/vt/mysqlctl/query.go @@ -18,6 +18,7 @@ package mysqlctl import ( "context" + "errors" "fmt" "strings" "time" @@ -226,6 +227,42 @@ func (mysqld *Mysqld) fetchStatuses(ctx context.Context, pattern string) (map[st return varMap, nil } +// ExecuteSuperQuery allows the user to execute a query as a super user. +func (mysqld *Mysqld) AcquireGlobalReadLock(ctx context.Context) error { + if mysqld.lockConn != nil { + return errors.New("lock already acquired") + } + + conn, err := getPoolReconnect(ctx, mysqld.dbaPool) + if err != nil { + return err + } + + err = mysqld.executeSuperQueryListConn(ctx, conn, []string{"FLUSH TABLES WITH READ LOCK"}) + if err != nil { + conn.Recycle() + return err + } + + mysqld.lockConn = conn + return nil +} + +func (mysqld *Mysqld) ReleaseGlobalReadLock(ctx context.Context) error { + if mysqld.lockConn == nil { + return errors.New("no read locks acquired yet") + } + + err := mysqld.executeSuperQueryListConn(ctx, mysqld.lockConn, []string{"UNLOCK TABLES"}) + if err != nil { + return err + } + + mysqld.lockConn.Recycle() + mysqld.lockConn = nil + return nil +} + const ( masterPasswordStart = " MASTER_PASSWORD = '" masterPasswordEnd = "',\n" diff --git a/go/vt/mysqlctl/xtrabackupengine.go b/go/vt/mysqlctl/xtrabackupengine.go index e6d02eedc1d..9e4917abb0f 100644 --- a/go/vt/mysqlctl/xtrabackupengine.go +++ b/go/vt/mysqlctl/xtrabackupengine.go @@ -779,23 +779,6 @@ func findReplicationPosition(input, flavor string, logger logutil.Logger) (repli return replicationPosition, nil } -// scanLinesToLogger scans full lines from the given Reader and sends them to -// the given Logger until EOF. -func scanLinesToLogger(prefix string, reader io.Reader, logger logutil.Logger, doneFunc func()) { - defer doneFunc() - - scanner := bufio.NewScanner(reader) - for scanner.Scan() { - line := scanner.Text() - logger.Infof("%s: %s", prefix, line) - } - if err := scanner.Err(); err != nil { - // This is usually run in a background goroutine, so there's no point - // returning an error. Just log it. - logger.Warningf("error scanning lines from %s: %v", prefix, err) - } -} - func stripeFileName(baseFileName string, index int) string { return fmt.Sprintf("%s-%03d", baseFileName, index) } @@ -959,6 +942,11 @@ func (be *XtrabackupEngine) ShouldDrainForBackup(req *tabletmanagerdatapb.Backup return false } +// ShouldStartMySQLAfterRestore signifies if this backup engine needs to restart MySQL once the restore is completed. +func (be *XtrabackupEngine) ShouldStartMySQLAfterRestore() bool { + return true +} + func init() { BackupRestoreEngineMap[xtrabackupEngineName] = &XtrabackupEngine{} } diff --git a/test/ci_workflow_gen.go b/test/ci_workflow_gen.go index bc36ad3753e..164d2c89e41 100644 --- a/test/ci_workflow_gen.go +++ b/test/ci_workflow_gen.go @@ -78,6 +78,7 @@ var ( "xb_backup", "backup_pitr", "backup_pitr_xtrabackup", + "backup_pitr_mysqlshell", "21", "22", "mysql_server_vault", diff --git a/test/config.json b/test/config.json index d21994ddca0..53ef22a5e8f 100644 --- a/test/config.json +++ b/test/config.json @@ -109,6 +109,15 @@ "RetryMax": 1, "Tags": [] }, + "backup_pitr_mysqlshell": { + "File": "unused.go", + "Args": ["vitess.io/vitess/go/test/endtoend/backup/pitr_mysqlshell", "-timeout", "30m"], + "Command": [], + "Manual": false, + "Shard": "backup_pitr_mysqlshell", + "RetryMax": 1, + "Tags": [] + }, "backup": { "File": "unused.go", "Args": ["vitess.io/vitess/go/test/endtoend/backup/vtctlbackup", "-timeout", "30m"], diff --git a/test/templates/cluster_endtoend_test.tpl b/test/templates/cluster_endtoend_test.tpl index eaad3a20b96..6c9c08c4c7d 100644 --- a/test/templates/cluster_endtoend_test.tpl +++ b/test/templates/cluster_endtoend_test.tpl @@ -120,7 +120,7 @@ jobs: sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update # Install everything else we need, and configure - sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 + sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 {{end}} From fce25c84aeee99d907bf2441cb6d4ffef3ac3235 Mon Sep 17 00:00:00 2001 From: Tanjin Xu Date: Tue, 29 Oct 2024 17:09:04 -0700 Subject: [PATCH 2/8] fix apis --- go/vt/mysqlctl/mysqlshellbackupengine.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/go/vt/mysqlctl/mysqlshellbackupengine.go b/go/vt/mysqlctl/mysqlshellbackupengine.go index 38dccc8c622..f3b3655e485 100644 --- a/go/vt/mysqlctl/mysqlshellbackupengine.go +++ b/go/vt/mysqlctl/mysqlshellbackupengine.go @@ -152,7 +152,7 @@ func (be *MySQLShellBackupEngine) ExecuteBackup(ctx context.Context, params Back } lockAcquired := time.Now() // we will report how long we hold the lock for - posBeforeBackup, err := params.Mysqld.PrimaryPosition(ctx) + posBeforeBackup, err := params.Mysqld.PrimaryPosition() if err != nil { return BackupUnusable, vterrors.Wrap(err, "failed to fetch position") } @@ -255,20 +255,20 @@ func (be *MySQLShellBackupEngine) ExecuteRestore(ctx context.Context, params Res } // make sure semi-sync is disabled, otherwise we will wait forever for acknowledgements - err = params.Mysqld.SetSemiSyncEnabled(ctx, false, false) + err = params.Mysqld.SetSemiSyncEnabled(false, false) if err != nil { return nil, vterrors.Wrap(err, "disable semi-sync failed") } params.Logger.Infof("restoring on an existing tablet, so dropping database %q", params.DbName) - readonly, err := params.Mysqld.IsSuperReadOnly(ctx) + readonly, err := params.Mysqld.IsSuperReadOnly() if err != nil { return nil, vterrors.Wrap(err, fmt.Sprintf("checking if mysqld has super_read_only=enable: %v", err)) } if readonly { - resetFunc, err := params.Mysqld.SetSuperReadOnly(ctx, false) + resetFunc, err := params.Mysqld.SetSuperReadOnly(false) if err != nil { return nil, vterrors.Wrap(err, fmt.Sprintf("unable to disable super-read-only: %v", err)) } @@ -466,7 +466,7 @@ func (be *MySQLShellBackupEngine) restorePreCheck(ctx context.Context, params Re } func (be *MySQLShellBackupEngine) handleSuperReadOnly(ctx context.Context, params RestoreParams) (func(), error) { - readonly, err := params.Mysqld.IsSuperReadOnly(ctx) + readonly, err := params.Mysqld.IsSuperReadOnly() if err != nil { return nil, vterrors.Wrap(err, fmt.Sprintf("checking if mysqld has super_read_only=enable: %v", err)) } @@ -474,7 +474,7 @@ func (be *MySQLShellBackupEngine) handleSuperReadOnly(ctx context.Context, param params.Logger.Infof("Is Super Read Only: %v", readonly) if readonly { - resetFunc, err := params.Mysqld.SetSuperReadOnly(ctx, false) + resetFunc, err := params.Mysqld.SetSuperReadOnly(false) if err != nil { return nil, vterrors.Wrap(err, fmt.Sprintf("unable to disable super-read-only: %v", err)) } From fd2e75eb1e19e4d6d1a4434d69580d6f4a2cfa5d Mon Sep 17 00:00:00 2001 From: Tanjin Xu Date: Tue, 29 Oct 2024 17:10:50 -0700 Subject: [PATCH 3/8] remove unneeded files --- changelog/21.0/21.0.0/summary.md | 174 ------------------------------- 1 file changed, 174 deletions(-) delete mode 100644 changelog/21.0/21.0.0/summary.md diff --git a/changelog/21.0/21.0.0/summary.md b/changelog/21.0/21.0.0/summary.md deleted file mode 100644 index 03f138a60c7..00000000000 --- a/changelog/21.0/21.0.0/summary.md +++ /dev/null @@ -1,174 +0,0 @@ -## Summary - -### Table of Contents - -- **[Major Changes](#major-changes)** - - **[Deprecations and Deletions](#deprecations-and-deletions)** - - [Deletion of deprecated metrics](#metric-deletion) - - [VTTablet Flags](#vttablet-flags) - - [Metrics](#deprecations-metrics) - - **[Traffic Mirroring](#traffic-mirroring)** - - **[New VTGate Shutdown Behavior](#new-vtgate-shutdown-behavior)** - - **[Tablet Throttler: Multi-Metric support](#tablet-throttler)** - - **[Allow Cross Cell Promotion in PRS](#allow-cross-cell)** - - **[Support for recursive CTEs](#recursive-cte)** - - **[VTGate Tablet Balancer](#tablet-balancer)** - - **[Query Timeout Override](#query-timeout)** - - **[New Backup Engine](#new-backup-engine)** - - **[Dynamic VReplication Configuration](#dynamic-vreplication-configuration)** - - **[Reference Table Materialization](#reference-table-materialization)** - -## Major Changes - -### Deprecations and Deletions - -#### Deletion of deprecated metrics - -The following metrics that were deprecated in the previous release, have now been deleted. - -| Metric Name | -|:--------------------------------------------:| -| `analysis.change.write` | -| `audit.write` | -| `discoveries.attempt` | -| `discoveries.fail` | -| `discoveries.instance_poll_seconds_exceeded` | -| `discoveries.queue_length` | -| `discoveries.recent_count` | -| `instance.read` | -| `instance.read_topology` | -| `emergency_reparent_counts` | -| `planned_reparent_counts` | -| `reparent_shard_operation_timings` | - -#### VTTablet Flags - -- `queryserver-enable-settings-pool` flag, added in v15, has been on by default since v17. - It is now deprecated and will be removed in a future release. - -#### Metrics - -The following metrics are now deprecated, if provided please use their replacement. - -| Component | Metric Name | Replaced By | -|------------|:---------------------:|:-------------------------------:| -| `vttablet` | `QueryCacheLength` | `QueryEnginePlanCacheLength` | -| `vttablet` | `QueryCacheSize` | `QueryEnginePlanCacheSize` | -| `vttablet` | `QueryCacheCapacity` | `QueryEnginePlanCacheCapacity` | -| `vttablet` | `QueryCacheEvictions` | `QueryEnginePlanCacheEvictions` | -| `vttablet` | `QueryCacheHits` | `QueryEnginePlanCacheHits` | -| `vttablet` | `QueryCacheMisses` | `QueryEnginePlanCacheMisses` | - -### Traffic Mirroring - -Traffic mirroring is intended to help reduce some of the uncertainty inherent to `MoveTables SwitchTraffic`. When -traffic mirroring is enabled, VTGate will mirror a percentage of traffic from one keyspace to another. - -Mirror rules may be enabled through `vtctldclient` with `MoveTables MirrorTraffic`. For example: - -```bash -$ vtctldclient --server :15999 MoveTables --target-keyspace customer --workflow commerce2customer MirrorTraffic --percent 5.0 -``` - -Mirror rules can be inspected with `GetMirrorRules`. - -### New VTGate Shutdown Behavior - -We added a new option to affect the VTGate shutdown process in v21 by using a connection drain timeout rather than the -older activity drain timeout. -The goal of this new behavior, connection draining option, is to disallow new connections when VTGate is shutting down, -but continue allowing existing connections to finish their work until they manually disconnect or until -the `--onterm_timeout` timeout is reached, -without getting a `Server shutdown in progress` error. - -This new behavior can be enabled by specifying the new `--mysql-server-drain-onterm` flag to VTGate. - -See more information about this change by [reading its RFC](https://github.com/vitessio/vitess/issues/15971). - -### Tablet Throttler: Multi-Metric support - -Up till `v20`, the tablet throttler would only monitor and use a single metric. That would be replication lag, by -default, or could be the result of a custom query. `v21` introduces a major redesign where the throttler monitors and -uses multiple metrics at the same time, including the above two. - -Backwards compatible with `v20`, the default behavior in `v21` is to monitor all metrics, but only use `lag` (if the -cutsom query is undefined) or the `cutsom` metric (if the custom query is defined). A `v20` `PRIMARY` is compatible with -a `v21` `REPLICA`, and a `v21` `PRIMARY` is compatible with a `v20` `REPLICA`. - -However, with `v21` it is possible to assign any combination of metrics (one or more) for a given app. The throttler -would then accept or reject the app's requests based on the health of _all_ assigned metrics. `v21` comes with a preset -list metrics, expected to be expanded: - -- `lag`: replication lag based on heartbeat injection. -- `threads_running`: concurrent active threads on the MySQL server. -- `loadavg`: per core load average measured on the tablet instance/pod. -- `custom`: the result of a custom query executed on the MySQL server. - -Each metric has a factory threshold which can be overridden by the `UpdateThrottlerConfig` command. - -The throttler also supports the catch-all `"all"` app name, and it is thus possible to assign metrics to _all_ apps. -Explicit app to metric assignments will override the catch-all configuration. - -Metrics are assigned a default _scope_, which could be `self` (isolated to the tablet) or `shard` (max, aka _worst_ -value among shard tablets). It is further possible to require a different scope for each metric. - -### Allow Cross Cell Promotion in PRS - -Up until now if the users wanted to promote a replica in a different cell than the current primary -using `PlannedReparentShard`, they had to specify the new primary with the `--new-primary` flag. - -We have now added a new flag `--allow-cross-cell-promotion` that lets `PlannedReparentShard` choose a primary in a -different cell even if no new primary is provided explicitly. - -### Experimental support for recursive CTEs - -We have added experimental support for recursive CTEs in Vitess. We are marking it as experimental because it is not yet -fully tested and may have some limitations. We are looking for feedback from the community to improve this feature. - -### VTGate Tablet Balancer - -When a VTGate routes a query and has multiple available tablets for a given shard / tablet type (e.g. REPLICA), the -current default behavior routes the query with local cell affinity and round robin policy. The VTGate Tablet Balancer -provides an alternate mechanism that routes queries to maintain an even distribution of query load to each tablet, while -preferentially routing to tablets in the same cell as the VTGate. - -The tablet balancer is enabled by a new flag `--enable-balancer` and configured by `--balancer-vtgate-cells` -and `--balancer-keyspaces`. - -See [RFC for details](https://github.com/vitessio/vitess/issues/12241). - -### Query Timeout Override - -VTGate sends an authoritative query timeout to VTTablet when the `QUERY_TIMEOUT_MS` comment directive, -`query_timeout` session system variable, or `query-timeout` flag is set. -The order of precedence is: `QUERY_TIMEOUT_MS` > `query_timeout` > `query-timeout`. -VTTablet overrides its default query timeout with the value received from VTGate. -All timeouts are specified in milliseconds. - -When a query is executed inside a transaction, this behavior does not apply; instead, -the smaller of the transaction timeout or the query timeout from VTGate is used. - -A query can also be set to have no timeout by using the `QUERY_TIMEOUT_MS` comment directive with a value of `0`. - -Example usage: -`select /*vt+ QUERY_TIMEOUT_MS=30 */ col from tbl` - -### New Backup Engine (EXPERIMENTAL) - -We are introducing a backup engine supporting logical backups starting on v21 to support use cases that require something else besides physical backups. This is experimental and is based on the -[MySQL Shell](https://dev.mysql.com/doc/mysql-shell/8.0/en/). - -The new engine is enabled by using `--backup_engine_implementation=mysqlshell`. There are other options that are required, so [check the docs](https://vitess.io/docs/21.0/user-guides/operating-vitess/backup-and-restore/creating-a-backup/) on which options are required and how to use it. - -### Dynamic VReplication Configuration - -Currently many of the configuration options for VReplication Workflows are vttablet flags. This means that any change -requires restarts of vttablets. We now allow these to be overridden while creating a workflow or dynamically once -the workflow is in progress. See https://github.com/vitessio/vitess/pull/16583 for details. - -### Reference Table Materialization - -There is a new option in [`Materialize` workflows](https://vitess.io/docs/reference/vreplication/materialize/) to keep -a synced copy of [reference or lookup tables](https://vitess.io/docs/reference/vreplication/reference_tables/) -(countries, states, zip_codes, etc) from an unsharded keyspace, which holds the source of truth for the reference -table, to all shards in a sharded keyspace. From 916595782a04803f9c8df503454a5fb55f301abf Mon Sep 17 00:00:00 2001 From: Tanjin Xu Date: Tue, 29 Oct 2024 17:17:41 -0700 Subject: [PATCH 4/8] fix docker file --- docker/lite/Dockerfile.mysql80 | 7 ++- ...ng new mysql shell backup engine (#16295)) | 63 ------------------- 2 files changed, 6 insertions(+), 64 deletions(-) delete mode 100644 docker/lite/Dockerfile~969e018d8d (adding new mysql shell backup engine (#16295)) diff --git a/docker/lite/Dockerfile.mysql80 b/docker/lite/Dockerfile.mysql80 index 1d57b5fe07e..d66003eefbe 100644 --- a/docker/lite/Dockerfile.mysql80 +++ b/docker/lite/Dockerfile.mysql80 @@ -35,13 +35,18 @@ RUN make install PREFIX=/vt/install # Start over and build the final image. FROM debian:bullseye-slim +# Install locale required for mysqlsh +RUN apt-get update && apt-get install -y locales \ + && echo "en_US.UTF-8 UTF-8" > /etc/locale.gen \ + && locale-gen en_US.UTF-8 + # Install dependencies COPY docker/lite/install_dependencies.sh /vt/dist/install_dependencies.sh RUN /vt/dist/install_dependencies.sh mysql80 # Set up Vitess user and directory tree. RUN groupadd -r vitess && useradd -r -g vitess vitess -RUN mkdir -p /vt/vtdataroot && chown -R vitess:vitess /vt +RUN mkdir -p /vt/vtdataroot /home/vitess && chown -R vitess:vitess /vt /home/vitess # Set up Vitess environment (just enough to run pre-built Go binaries) ENV VTROOT /vt/src/vitess.io/vitess diff --git a/docker/lite/Dockerfile~969e018d8d (adding new mysql shell backup engine (#16295)) b/docker/lite/Dockerfile~969e018d8d (adding new mysql shell backup engine (#16295)) deleted file mode 100644 index 9d2b90efda2..00000000000 --- a/docker/lite/Dockerfile~969e018d8d (adding new mysql shell backup engine (#16295)) +++ /dev/null @@ -1,63 +0,0 @@ -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -FROM --platform=linux/amd64 golang:1.23.1-bullseye AS builder - -# Allows docker builds to set the BUILD_NUMBER -ARG BUILD_NUMBER - -WORKDIR /vt/src/vitess.io/vitess - -# Create vitess user -RUN groupadd -r vitess && useradd -r -g vitess vitess -RUN mkdir -p /vt/vtdataroot /home/vitess -RUN chown -R vitess:vitess /vt /home/vitess -USER vitess - -# Re-copy sources from working tree. -COPY --chown=vitess:vitess . /vt/src/vitess.io/vitess - -RUN make install PREFIX=/vt/install - -# Start over and build the final image. -FROM --platform=linux/amd64 debian:bullseye-slim - -# Install locale required for mysqlsh -RUN apt-get update && apt-get install -y locales \ - && echo "en_US.UTF-8 UTF-8" > /etc/locale.gen \ - && locale-gen en_US.UTF-8 - -# Install dependencies -COPY docker/utils/install_dependencies.sh /vt/dist/install_dependencies.sh -RUN /vt/dist/install_dependencies.sh mysql80 - -# Set up Vitess user and directory tree. -RUN groupadd -r vitess && useradd -r -g vitess vitess -RUN mkdir -p /vt/vtdataroot /home/vitess && chown -R vitess:vitess /vt /home/vitess - -# Set up Vitess environment (just enough to run pre-built Go binaries) -ENV VTROOT /vt -ENV VTDATAROOT /vt/vtdataroot -ENV PATH $VTROOT/bin:$PATH - -# Copy artifacts from builder layer. -COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt -COPY --from=builder --chown=vitess:vitess /vt/install /vt -COPY --from=builder --chown=vitess:vitess /vt/src/vitess.io/vitess/web/vtadmin /vt/web/vtadmin -COPY --from=builder --chown=vitess:vitess /vt/src/vitess.io/vitess/config/init_db.sql /vt/config/ -COPY --from=builder --chown=vitess:vitess /vt/src/vitess.io/vitess/config/mycnf /vt/config/ - -# Create mount point for actual data (e.g. MySQL data dir) -VOLUME /vt/vtdataroot -USER vitess From e1e28c2ecb6b1a5e1404471b7fb2d2403bdf70d2 Mon Sep 17 00:00:00 2001 From: Tanjin Xu Date: Tue, 29 Oct 2024 17:55:00 -0700 Subject: [PATCH 5/8] remove mariadb test --- test/config.json | 9 --------- 1 file changed, 9 deletions(-) diff --git a/test/config.json b/test/config.json index 53ef22a5e8f..8a520d03e26 100644 --- a/test/config.json +++ b/test/config.json @@ -1247,15 +1247,6 @@ "RetryMax": 1, "Tags": [] }, - "vreplication_mariadb_to_mysql": { - "File": "unused.go", - "Args": ["vitess.io/vitess/go/test/endtoend/vreplication", "-run", "TestMoveTablesMariaDBToMySQL", "-timeout", "20m"], - "Command": [], - "Manual": false, - "Shard": "vreplication_across_db_versions", - "RetryMax": 1, - "Tags": [] - }, "vreplication_vtctldclient_cli": { "File": "unused.go", "Args": ["vitess.io/vitess/go/test/endtoend/vreplication", "-run", "TestVtctldclientCLI", "-timeout", "20m"], From 4339d83d1dc78febcc1f5b6d3cb96f842a8f73c9 Mon Sep 17 00:00:00 2001 From: Tanjin Xu Date: Tue, 29 Oct 2024 17:56:27 -0700 Subject: [PATCH 6/8] Revert "remove mariadb test" This reverts commit cd9bc6844144137b532eb2e6737ad385b4cb66a4. --- test/config.json | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/config.json b/test/config.json index 8a520d03e26..53ef22a5e8f 100644 --- a/test/config.json +++ b/test/config.json @@ -1247,6 +1247,15 @@ "RetryMax": 1, "Tags": [] }, + "vreplication_mariadb_to_mysql": { + "File": "unused.go", + "Args": ["vitess.io/vitess/go/test/endtoend/vreplication", "-run", "TestMoveTablesMariaDBToMySQL", "-timeout", "20m"], + "Command": [], + "Manual": false, + "Shard": "vreplication_across_db_versions", + "RetryMax": 1, + "Tags": [] + }, "vreplication_vtctldclient_cli": { "File": "unused.go", "Args": ["vitess.io/vitess/go/test/endtoend/vreplication", "-run", "TestVtctldclientCLI", "-timeout", "20m"], From 04cc9cc93d073aa745eace6579a9ede155873b7a Mon Sep 17 00:00:00 2001 From: Tanjin Xu Date: Wed, 13 Nov 2024 16:46:01 -0800 Subject: [PATCH 7/8] fix backup message --- go/test/endtoend/backup/vtctlbackup/backup_utils.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go/test/endtoend/backup/vtctlbackup/backup_utils.go b/go/test/endtoend/backup/vtctlbackup/backup_utils.go index e2c1e17bebe..8a0dc2ce7ec 100644 --- a/go/test/endtoend/backup/vtctlbackup/backup_utils.go +++ b/go/test/endtoend/backup/vtctlbackup/backup_utils.go @@ -1047,7 +1047,7 @@ func verifySemiSyncStatus(t *testing.T, vttablet *cluster.Vttablet, expectedStat } func terminateBackup(t *testing.T, alias string) { - stopBackupMsg := "Completed backing up" + stopBackupMsg := "Done taking Backup" if currentSetupType == XtraBackup { stopBackupMsg = "Starting backup with" } From 82df135a2a97200619b286e2f2ab4fb2cd75111b Mon Sep 17 00:00:00 2001 From: Tanjin Xu Date: Wed, 13 Nov 2024 16:57:03 -0800 Subject: [PATCH 8/8] fix tests --- .github/workflows/cluster_endtoend_backup_pitr_mysqlshell.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cluster_endtoend_backup_pitr_mysqlshell.yml b/.github/workflows/cluster_endtoend_backup_pitr_mysqlshell.yml index ffcf6cdb4c0..bbc384993df 100644 --- a/.github/workflows/cluster_endtoend_backup_pitr_mysqlshell.yml +++ b/.github/workflows/cluster_endtoend_backup_pitr_mysqlshell.yml @@ -73,7 +73,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v5 with: - go-version: 1.22.5 + go-version: 1.22.8 - name: Setup github.com/slackhq/vitess-addons access token if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'