diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 6778ec7e0de..7b9b54a19bb 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -28,6 +28,7 @@ go.sum @ajm188 @deepthi @harshit-gangal @mattlord @rohit-nayak-ps @systay @froui /go/test/endtoend/onlineddl @rohit-nayak-ps @shlomi-noach /go/test/endtoend/messaging @mattlord @rohit-nayak-ps @derekperkins /go/test/endtoend/schemadiff @shlomi-noach @mattlord +/go/test/endtoend/transaction @harshit-gangal @systay @frouioui @GuptaManan100 /go/test/endtoend/*throttler* @shlomi-noach @mattlord @timvaillancourt /go/test/endtoend/vtgate @harshit-gangal @systay @frouioui /go/test/endtoend/vtorc @deepthi @shlomi-noach @GuptaManan100 @timvaillancourt diff --git a/.github/workflows/assign_milestone.yml b/.github/workflows/assign_milestone.yml index 3ae0a89b4a9..6687a239b57 100644 --- a/.github/workflows/assign_milestone.yml +++ b/.github/workflows/assign_milestone.yml @@ -12,7 +12,7 @@ env: jobs: build: name: Assign Milestone - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 permissions: pull-requests: write diff --git a/.github/workflows/auto_approve_pr.yml b/.github/workflows/auto_approve_pr.yml index de553694927..e584337f78b 100644 --- a/.github/workflows/auto_approve_pr.yml +++ b/.github/workflows/auto_approve_pr.yml @@ -9,7 +9,7 @@ permissions: jobs: auto_approve: name: Auto Approve Pull Request - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 permissions: pull-requests: write # only given on local PRs, forks run with `read` access diff --git a/.github/workflows/check_label.yml b/.github/workflows/check_label.yml index 15b3ae3ec15..2c6439a29f9 100644 --- a/.github/workflows/check_label.yml +++ b/.github/workflows/check_label.yml @@ -9,7 +9,7 @@ jobs: check_pull_request_labels: name: Check Pull Request labels timeout-minutes: 10 - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 if: github.repository == 'vitessio/vitess' steps: - name: Release Notes label diff --git a/.github/workflows/check_make_vtadmin_authz_testgen.yml b/.github/workflows/check_make_vtadmin_authz_testgen.yml index f69df918be1..26543c8a948 100644 --- a/.github/workflows/check_make_vtadmin_authz_testgen.yml +++ b/.github/workflows/check_make_vtadmin_authz_testgen.yml @@ -6,7 +6,7 @@ permissions: read-all jobs: build: name: Check Make vtadmin_authz_testgen - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI run: | @@ -61,7 +61,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.vtadmin_changes == 'true' run: | sudo apt-get update - sudo apt-get install -y make unzip g++ etcd curl git wget + sudo apt-get install -y make unzip g++ etcd-client etcd-server curl git wget sudo service etcd stop go mod download go install golang.org/x/tools/cmd/goimports@latest diff --git a/.github/workflows/check_make_vtadmin_web_proto.yml b/.github/workflows/check_make_vtadmin_web_proto.yml index 0f8d404a256..1c1bf2cae4f 100644 --- a/.github/workflows/check_make_vtadmin_web_proto.yml +++ b/.github/workflows/check_make_vtadmin_web_proto.yml @@ -6,7 +6,7 @@ permissions: read-all jobs: build: name: Check Make VTAdmin Web Proto - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI run: | diff --git a/.github/workflows/close_stale_pull_requests.yml b/.github/workflows/close_stale_pull_requests.yml index 17fef550aa3..dbb71ac79b9 100644 --- a/.github/workflows/close_stale_pull_requests.yml +++ b/.github/workflows/close_stale_pull_requests.yml @@ -9,7 +9,7 @@ permissions: read-all jobs: close_stale_pull_requests: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 permissions: pull-requests: write diff --git a/.github/workflows/cluster_endtoend_12.yml b/.github/workflows/cluster_endtoend_12.yml index 236e6e2835c..7c98855b783 100644 --- a/.github/workflows/cluster_endtoend_12.yml +++ b/.github/workflows/cluster_endtoend_12.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (12) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # 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 + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_13.yml b/.github/workflows/cluster_endtoend_13.yml index 945d3448287..947d393bbbb 100644 --- a/.github/workflows/cluster_endtoend_13.yml +++ b/.github/workflows/cluster_endtoend_13.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (13) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # 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 + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_15.yml b/.github/workflows/cluster_endtoend_15.yml index 0c57e651013..aedc1bbd691 100644 --- a/.github/workflows/cluster_endtoend_15.yml +++ b/.github/workflows/cluster_endtoend_15.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (15) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # 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 + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_18.yml b/.github/workflows/cluster_endtoend_18.yml index 0c04f3e1b76..9886b617d51 100644 --- a/.github/workflows/cluster_endtoend_18.yml +++ b/.github/workflows/cluster_endtoend_18.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (18) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # 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 + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_21.yml b/.github/workflows/cluster_endtoend_21.yml index 7d14a8b99ed..5d703099a3d 100644 --- a/.github/workflows/cluster_endtoend_21.yml +++ b/.github/workflows/cluster_endtoend_21.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (21) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # 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 + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_backup_pitr.yml b/.github/workflows/cluster_endtoend_backup_pitr.yml index 99047933d69..1f744e85004 100644 --- a/.github/workflows/cluster_endtoend_backup_pitr.yml +++ b/.github/workflows/cluster_endtoend_backup_pitr.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (backup_pitr) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # 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 + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_backup_pitr_mysqlshell.yml b/.github/workflows/cluster_endtoend_backup_pitr_mysqlshell.yml index 8da7e054666..6ed73f260df 100644 --- a/.github/workflows/cluster_endtoend_backup_pitr_mysqlshell.yml +++ b/.github/workflows/cluster_endtoend_backup_pitr_mysqlshell.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (backup_pitr_mysqlshell) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # 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 + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_backup_pitr_xtrabackup.yml b/.github/workflows/cluster_endtoend_backup_pitr_xtrabackup.yml index 4012d1b1e4d..9bb6a4b56f8 100644 --- a/.github/workflows/cluster_endtoend_backup_pitr_xtrabackup.yml +++ b/.github/workflows/cluster_endtoend_backup_pitr_xtrabackup.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (backup_pitr_xtrabackup) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI diff --git a/.github/workflows/cluster_endtoend_ers_prs_newfeatures_heavy.yml b/.github/workflows/cluster_endtoend_ers_prs_newfeatures_heavy.yml index 177fc77a5c4..e859749ceca 100644 --- a/.github/workflows/cluster_endtoend_ers_prs_newfeatures_heavy.yml +++ b/.github/workflows/cluster_endtoend_ers_prs_newfeatures_heavy.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (ers_prs_newfeatures_heavy) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # 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 + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_mysql80.yml b/.github/workflows/cluster_endtoend_mysql80.yml index d0ff268c5fb..653db141b7d 100644 --- a/.github/workflows/cluster_endtoend_mysql80.yml +++ b/.github/workflows/cluster_endtoend_mysql80.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (mysql80) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # 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 + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_mysql_server_vault.yml b/.github/workflows/cluster_endtoend_mysql_server_vault.yml index 38cdcbadf95..9de4aacd499 100644 --- a/.github/workflows/cluster_endtoend_mysql_server_vault.yml +++ b/.github/workflows/cluster_endtoend_mysql_server_vault.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (mysql_server_vault) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # 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 + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_onlineddl_revert.yml b/.github/workflows/cluster_endtoend_onlineddl_revert.yml index dd9990b4761..54c85944190 100644 --- a/.github/workflows/cluster_endtoend_onlineddl_revert.yml +++ b/.github/workflows/cluster_endtoend_onlineddl_revert.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (onlineddl_revert) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -97,10 +97,19 @@ jobs: # 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 + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_onlineddl_scheduler.yml b/.github/workflows/cluster_endtoend_onlineddl_scheduler.yml index 144e06915ce..9e117ac0f04 100644 --- a/.github/workflows/cluster_endtoend_onlineddl_scheduler.yml +++ b/.github/workflows/cluster_endtoend_onlineddl_scheduler.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (onlineddl_scheduler) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -97,10 +97,19 @@ jobs: # 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 + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_onlineddl_vrepl.yml b/.github/workflows/cluster_endtoend_onlineddl_vrepl.yml index 5cd394f2d42..5056e46d895 100644 --- a/.github/workflows/cluster_endtoend_onlineddl_vrepl.yml +++ b/.github/workflows/cluster_endtoend_onlineddl_vrepl.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (onlineddl_vrepl) - runs-on: gh-hosted-runners-16cores-1 + runs-on: gh-hosted-runners-16cores-1-24.04 steps: - name: Skip CI @@ -97,10 +97,19 @@ jobs: # 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 + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_onlineddl_vrepl_stress.yml b/.github/workflows/cluster_endtoend_onlineddl_vrepl_stress.yml index 993a2d5193e..3597b37ede7 100644 --- a/.github/workflows/cluster_endtoend_onlineddl_vrepl_stress.yml +++ b/.github/workflows/cluster_endtoend_onlineddl_vrepl_stress.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (onlineddl_vrepl_stress) - runs-on: gh-hosted-runners-16cores-1 + runs-on: gh-hosted-runners-16cores-1-24.04 steps: - name: Skip CI @@ -97,10 +97,19 @@ jobs: # 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 + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_onlineddl_vrepl_stress_suite.yml b/.github/workflows/cluster_endtoend_onlineddl_vrepl_stress_suite.yml index 015db29723e..a5322578981 100644 --- a/.github/workflows/cluster_endtoend_onlineddl_vrepl_stress_suite.yml +++ b/.github/workflows/cluster_endtoend_onlineddl_vrepl_stress_suite.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (onlineddl_vrepl_stress_suite) - runs-on: gh-hosted-runners-16cores-1 + runs-on: gh-hosted-runners-16cores-1-24.04 steps: - name: Skip CI @@ -97,10 +97,19 @@ jobs: # 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 + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_onlineddl_vrepl_suite.yml b/.github/workflows/cluster_endtoend_onlineddl_vrepl_suite.yml index 550408f5def..12bde509048 100644 --- a/.github/workflows/cluster_endtoend_onlineddl_vrepl_suite.yml +++ b/.github/workflows/cluster_endtoend_onlineddl_vrepl_suite.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (onlineddl_vrepl_suite) - runs-on: gh-hosted-runners-16cores-1 + runs-on: gh-hosted-runners-16cores-1-24.04 steps: - name: Skip CI @@ -97,10 +97,19 @@ jobs: # 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 + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_schemadiff_vrepl.yml b/.github/workflows/cluster_endtoend_schemadiff_vrepl.yml index 9c2cac74843..e452d28606a 100644 --- a/.github/workflows/cluster_endtoend_schemadiff_vrepl.yml +++ b/.github/workflows/cluster_endtoend_schemadiff_vrepl.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (schemadiff_vrepl) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -97,10 +97,19 @@ jobs: # 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 + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_tabletmanager_consul.yml b/.github/workflows/cluster_endtoend_tabletmanager_consul.yml index 4846fdcc2ac..3e8ffce0ac5 100644 --- a/.github/workflows/cluster_endtoend_tabletmanager_consul.yml +++ b/.github/workflows/cluster_endtoend_tabletmanager_consul.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (tabletmanager_consul) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # 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 + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_tabletmanager_tablegc.yml b/.github/workflows/cluster_endtoend_tabletmanager_tablegc.yml index 435d28a722a..cc75ef22937 100644 --- a/.github/workflows/cluster_endtoend_tabletmanager_tablegc.yml +++ b/.github/workflows/cluster_endtoend_tabletmanager_tablegc.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (tabletmanager_tablegc) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # 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 + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_tabletmanager_throttler_topo.yml b/.github/workflows/cluster_endtoend_tabletmanager_throttler_topo.yml index 6cf480aa1f1..0837be5356e 100644 --- a/.github/workflows/cluster_endtoend_tabletmanager_throttler_topo.yml +++ b/.github/workflows/cluster_endtoend_tabletmanager_throttler_topo.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (tabletmanager_throttler_topo) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # 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 + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_topo_connection_cache.yml b/.github/workflows/cluster_endtoend_topo_connection_cache.yml index 056bfb6d8a4..da6ee53ad53 100644 --- a/.github/workflows/cluster_endtoend_topo_connection_cache.yml +++ b/.github/workflows/cluster_endtoend_topo_connection_cache.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (topo_connection_cache) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # 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 + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_vreplication_across_db_versions.yml b/.github/workflows/cluster_endtoend_vreplication_across_db_versions.yml index aacf9d157fd..a38506ddd7d 100644 --- a/.github/workflows/cluster_endtoend_vreplication_across_db_versions.yml +++ b/.github/workflows/cluster_endtoend_vreplication_across_db_versions.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (vreplication_across_db_versions) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # 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 + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_vreplication_basic.yml b/.github/workflows/cluster_endtoend_vreplication_basic.yml index ead3b816df2..283d0451a34 100644 --- a/.github/workflows/cluster_endtoend_vreplication_basic.yml +++ b/.github/workflows/cluster_endtoend_vreplication_basic.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (vreplication_basic) - runs-on: gh-hosted-runners-16cores-1 + runs-on: gh-hosted-runners-16cores-1-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # 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 + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_vreplication_cellalias.yml b/.github/workflows/cluster_endtoend_vreplication_cellalias.yml index d9f1aeac45e..b316c075614 100644 --- a/.github/workflows/cluster_endtoend_vreplication_cellalias.yml +++ b/.github/workflows/cluster_endtoend_vreplication_cellalias.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (vreplication_cellalias) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # 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 + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_vreplication_copy_parallel.yml b/.github/workflows/cluster_endtoend_vreplication_copy_parallel.yml index 9cdc4a9091a..48f506709e7 100644 --- a/.github/workflows/cluster_endtoend_vreplication_copy_parallel.yml +++ b/.github/workflows/cluster_endtoend_vreplication_copy_parallel.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (vreplication_copy_parallel) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # 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 + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_vreplication_foreign_key_stress.yml b/.github/workflows/cluster_endtoend_vreplication_foreign_key_stress.yml index f95f70e8550..a1293e3688e 100644 --- a/.github/workflows/cluster_endtoend_vreplication_foreign_key_stress.yml +++ b/.github/workflows/cluster_endtoend_vreplication_foreign_key_stress.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (vreplication_foreign_key_stress) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # 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 + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_vreplication_mariadb_to_mysql.yml b/.github/workflows/cluster_endtoend_vreplication_mariadb_to_mysql.yml index 2a6af9b91bd..2013d89a83d 100644 --- a/.github/workflows/cluster_endtoend_vreplication_mariadb_to_mysql.yml +++ b/.github/workflows/cluster_endtoend_vreplication_mariadb_to_mysql.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (vreplication_mariadb_to_mysql) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # 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 + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_vreplication_migrate.yml b/.github/workflows/cluster_endtoend_vreplication_migrate.yml index 6b6768eb676..e7a8a8cb5ce 100644 --- a/.github/workflows/cluster_endtoend_vreplication_migrate.yml +++ b/.github/workflows/cluster_endtoend_vreplication_migrate.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (vreplication_migrate) - runs-on: gh-hosted-runners-16cores-1 + runs-on: gh-hosted-runners-16cores-1-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # 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 + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_vreplication_multi_tenant.yml b/.github/workflows/cluster_endtoend_vreplication_multi_tenant.yml index 78b6c616691..c0c80a8dc61 100644 --- a/.github/workflows/cluster_endtoend_vreplication_multi_tenant.yml +++ b/.github/workflows/cluster_endtoend_vreplication_multi_tenant.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (vreplication_multi_tenant) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # 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 + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 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 30367611574..0b80b82fd7f 100644 --- a/.github/workflows/cluster_endtoend_vreplication_partial_movetables_and_materialize.yml +++ b/.github/workflows/cluster_endtoend_vreplication_partial_movetables_and_materialize.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (vreplication_partial_movetables_and_materialize) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # 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 + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_vreplication_v2.yml b/.github/workflows/cluster_endtoend_vreplication_v2.yml index 23871ada3b8..fcbe7057b4c 100644 --- a/.github/workflows/cluster_endtoend_vreplication_v2.yml +++ b/.github/workflows/cluster_endtoend_vreplication_v2.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (vreplication_v2) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # 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 + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_vreplication_vtctldclient_vdiff2_movetables_tz.yml b/.github/workflows/cluster_endtoend_vreplication_vtctldclient_vdiff2_movetables_tz.yml index dd1eb76ca0e..40f2002f9a3 100644 --- a/.github/workflows/cluster_endtoend_vreplication_vtctldclient_vdiff2_movetables_tz.yml +++ b/.github/workflows/cluster_endtoend_vreplication_vtctldclient_vdiff2_movetables_tz.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (vreplication_vtctldclient_vdiff2_movetables_tz) - runs-on: gh-hosted-runners-16cores-1 + runs-on: gh-hosted-runners-16cores-1-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # 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 + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_vstream.yml b/.github/workflows/cluster_endtoend_vstream.yml index 3c99a687abc..acfefb40758 100644 --- a/.github/workflows/cluster_endtoend_vstream.yml +++ b/.github/workflows/cluster_endtoend_vstream.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (vstream) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # 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 + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_vtbackup.yml b/.github/workflows/cluster_endtoend_vtbackup.yml index e47e8fbad47..947598bc307 100644 --- a/.github/workflows/cluster_endtoend_vtbackup.yml +++ b/.github/workflows/cluster_endtoend_vtbackup.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (vtbackup) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # 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 + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_vtctlbackup_sharded_clustertest_heavy.yml b/.github/workflows/cluster_endtoend_vtctlbackup_sharded_clustertest_heavy.yml index 8f9a27bc750..c6f8712a35a 100644 --- a/.github/workflows/cluster_endtoend_vtctlbackup_sharded_clustertest_heavy.yml +++ b/.github/workflows/cluster_endtoend_vtctlbackup_sharded_clustertest_heavy.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (vtctlbackup_sharded_clustertest_heavy) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # 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 + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_vtgate_concurrentdml.yml b/.github/workflows/cluster_endtoend_vtgate_concurrentdml.yml index db9ea451081..2cd81fe04ef 100644 --- a/.github/workflows/cluster_endtoend_vtgate_concurrentdml.yml +++ b/.github/workflows/cluster_endtoend_vtgate_concurrentdml.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (vtgate_concurrentdml) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # 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 + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_vtgate_foreignkey_stress.yml b/.github/workflows/cluster_endtoend_vtgate_foreignkey_stress.yml index 2fada310f7e..19620d9e3a2 100644 --- a/.github/workflows/cluster_endtoend_vtgate_foreignkey_stress.yml +++ b/.github/workflows/cluster_endtoend_vtgate_foreignkey_stress.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (vtgate_foreignkey_stress) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # 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 + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_vtgate_gen4.yml b/.github/workflows/cluster_endtoend_vtgate_gen4.yml index 04813704cd2..06961af4ee7 100644 --- a/.github/workflows/cluster_endtoend_vtgate_gen4.yml +++ b/.github/workflows/cluster_endtoend_vtgate_gen4.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (vtgate_gen4) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # 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 + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_vtgate_general_heavy.yml b/.github/workflows/cluster_endtoend_vtgate_general_heavy.yml index bdb6b8b149d..e0b21df421a 100644 --- a/.github/workflows/cluster_endtoend_vtgate_general_heavy.yml +++ b/.github/workflows/cluster_endtoend_vtgate_general_heavy.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (vtgate_general_heavy) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # 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 + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_vtgate_godriver.yml b/.github/workflows/cluster_endtoend_vtgate_godriver.yml index e23742a5650..23c9da550c2 100644 --- a/.github/workflows/cluster_endtoend_vtgate_godriver.yml +++ b/.github/workflows/cluster_endtoend_vtgate_godriver.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (vtgate_godriver) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # 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 + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_vtgate_partial_keyspace.yml b/.github/workflows/cluster_endtoend_vtgate_partial_keyspace.yml index 4d082900790..f5d1850aca3 100644 --- a/.github/workflows/cluster_endtoend_vtgate_partial_keyspace.yml +++ b/.github/workflows/cluster_endtoend_vtgate_partial_keyspace.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (vtgate_partial_keyspace) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # 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 + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_vtgate_queries.yml b/.github/workflows/cluster_endtoend_vtgate_queries.yml index 46242a6b7f4..80462086a57 100644 --- a/.github/workflows/cluster_endtoend_vtgate_queries.yml +++ b/.github/workflows/cluster_endtoend_vtgate_queries.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (vtgate_queries) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # 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 + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_vtgate_readafterwrite.yml b/.github/workflows/cluster_endtoend_vtgate_readafterwrite.yml index b39aa3693e6..686db1e12ab 100644 --- a/.github/workflows/cluster_endtoend_vtgate_readafterwrite.yml +++ b/.github/workflows/cluster_endtoend_vtgate_readafterwrite.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (vtgate_readafterwrite) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # 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 + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_vtgate_reservedconn.yml b/.github/workflows/cluster_endtoend_vtgate_reservedconn.yml index 0032704b240..0f4ae20b0a7 100644 --- a/.github/workflows/cluster_endtoend_vtgate_reservedconn.yml +++ b/.github/workflows/cluster_endtoend_vtgate_reservedconn.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (vtgate_reservedconn) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # 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 + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_vtgate_schema.yml b/.github/workflows/cluster_endtoend_vtgate_schema.yml index be619a0815c..2dca36298e1 100644 --- a/.github/workflows/cluster_endtoend_vtgate_schema.yml +++ b/.github/workflows/cluster_endtoend_vtgate_schema.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (vtgate_schema) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # 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 + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_vtgate_schema_tracker.yml b/.github/workflows/cluster_endtoend_vtgate_schema_tracker.yml index a02b4ec5435..711f38253cb 100644 --- a/.github/workflows/cluster_endtoend_vtgate_schema_tracker.yml +++ b/.github/workflows/cluster_endtoend_vtgate_schema_tracker.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (vtgate_schema_tracker) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # 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 + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_vtgate_tablet_healthcheck_cache.yml b/.github/workflows/cluster_endtoend_vtgate_tablet_healthcheck_cache.yml index ff0c6990512..55996d7fa86 100644 --- a/.github/workflows/cluster_endtoend_vtgate_tablet_healthcheck_cache.yml +++ b/.github/workflows/cluster_endtoend_vtgate_tablet_healthcheck_cache.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (vtgate_tablet_healthcheck_cache) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # 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 + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_vtgate_topo.yml b/.github/workflows/cluster_endtoend_vtgate_topo.yml index e0f4397d56c..f47e0a8dd63 100644 --- a/.github/workflows/cluster_endtoend_vtgate_topo.yml +++ b/.github/workflows/cluster_endtoend_vtgate_topo.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (vtgate_topo) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # 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 + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_vtgate_topo_consul.yml b/.github/workflows/cluster_endtoend_vtgate_topo_consul.yml index a10a9947218..1b62ac79041 100644 --- a/.github/workflows/cluster_endtoend_vtgate_topo_consul.yml +++ b/.github/workflows/cluster_endtoend_vtgate_topo_consul.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (vtgate_topo_consul) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # 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 + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_vtgate_topo_etcd.yml b/.github/workflows/cluster_endtoend_vtgate_topo_etcd.yml index 42e3b3d919c..f88d3b9af3a 100644 --- a/.github/workflows/cluster_endtoend_vtgate_topo_etcd.yml +++ b/.github/workflows/cluster_endtoend_vtgate_topo_etcd.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (vtgate_topo_etcd) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # 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 + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_vtgate_transaction.yml b/.github/workflows/cluster_endtoend_vtgate_transaction.yml index bc400dde33f..9ae7b465695 100644 --- a/.github/workflows/cluster_endtoend_vtgate_transaction.yml +++ b/.github/workflows/cluster_endtoend_vtgate_transaction.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (vtgate_transaction) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # 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 + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_vtgate_unsharded.yml b/.github/workflows/cluster_endtoend_vtgate_unsharded.yml index 8c128e95b0f..d348606dc31 100644 --- a/.github/workflows/cluster_endtoend_vtgate_unsharded.yml +++ b/.github/workflows/cluster_endtoend_vtgate_unsharded.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (vtgate_unsharded) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # 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 + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_vtgate_vindex_heavy.yml b/.github/workflows/cluster_endtoend_vtgate_vindex_heavy.yml index e260443a242..16bdee60217 100644 --- a/.github/workflows/cluster_endtoend_vtgate_vindex_heavy.yml +++ b/.github/workflows/cluster_endtoend_vtgate_vindex_heavy.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (vtgate_vindex_heavy) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # 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 + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_vtgate_vschema.yml b/.github/workflows/cluster_endtoend_vtgate_vschema.yml index 72e9ac45949..fb176119769 100644 --- a/.github/workflows/cluster_endtoend_vtgate_vschema.yml +++ b/.github/workflows/cluster_endtoend_vtgate_vschema.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (vtgate_vschema) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # 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 + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_vtorc.yml b/.github/workflows/cluster_endtoend_vtorc.yml index 23544b28d01..92c2ea3d5d7 100644 --- a/.github/workflows/cluster_endtoend_vtorc.yml +++ b/.github/workflows/cluster_endtoend_vtorc.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (vtorc) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -105,10 +105,19 @@ jobs: # 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 + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_vttablet_prscomplex.yml b/.github/workflows/cluster_endtoend_vttablet_prscomplex.yml index eaf6583a22d..9868c382a69 100644 --- a/.github/workflows/cluster_endtoend_vttablet_prscomplex.yml +++ b/.github/workflows/cluster_endtoend_vttablet_prscomplex.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (vttablet_prscomplex) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # 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 + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_xb_backup.yml b/.github/workflows/cluster_endtoend_xb_backup.yml index dae2eca922f..26dbfea2def 100644 --- a/.github/workflows/cluster_endtoend_xb_backup.yml +++ b/.github/workflows/cluster_endtoend_xb_backup.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (xb_backup) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI diff --git a/.github/workflows/cluster_endtoend_xb_recovery.yml b/.github/workflows/cluster_endtoend_xb_recovery.yml index 0dad33aee7f..43360690d6b 100644 --- a/.github/workflows/cluster_endtoend_xb_recovery.yml +++ b/.github/workflows/cluster_endtoend_xb_recovery.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (xb_recovery) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI diff --git a/.github/workflows/code_freeze.yml b/.github/workflows/code_freeze.yml index 92ef037ee6b..a66fb6e8b2b 100644 --- a/.github/workflows/code_freeze.yml +++ b/.github/workflows/code_freeze.yml @@ -7,7 +7,7 @@ permissions: read-all jobs: build: name: Code Freeze - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Fail if Code Freeze is enabled run: | diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml index 7846749a013..fc30974212b 100644 --- a/.github/workflows/codecov.yml +++ b/.github/workflows/codecov.yml @@ -9,7 +9,7 @@ permissions: read-all jobs: test: name: Code Coverage - runs-on: gh-hosted-runners-16cores-1 + runs-on: gh-hosted-runners-16cores-1-24.04 steps: - name: Check out code diff --git a/.github/workflows/codeql_analysis.yml b/.github/workflows/codeql_analysis.yml index e1a474ce9a2..c523e34c697 100644 --- a/.github/workflows/codeql_analysis.yml +++ b/.github/workflows/codeql_analysis.yml @@ -14,7 +14,7 @@ permissions: read-all jobs: analyze: name: Analyze - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 permissions: actions: read contents: read @@ -59,13 +59,13 @@ jobs: sudo rm -rf /etc/mysql # Install mysql80 sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 sudo DEBIAN_FRONTEND="noninteractive" apt-get install -y mysql-server mysql-client # Install everything else we need, and configure - sudo apt-get install -y make unzip g++ etcd curl git wget eatmydata + sudo apt-get install -y make unzip g++ etcd-client etcd-server curl git wget eatmydata sudo service mysql stop sudo service etcd stop sudo bash -c "echo '/usr/sbin/mysqld { }' > /etc/apparmor.d/usr.sbin.mysqld" # https://bugs.launchpad.net/ubuntu/+source/mariadb-10.1/+bug/1806263 diff --git a/.github/workflows/create_release.yml b/.github/workflows/create_release.yml index a42bbc0ff2d..424cc5bfe15 100644 --- a/.github/workflows/create_release.yml +++ b/.github/workflows/create_release.yml @@ -12,7 +12,7 @@ permissions: read-all jobs: build: name: Create Release - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 permissions: contents: write steps: diff --git a/.github/workflows/docker_build_images.yml b/.github/workflows/docker_build_images.yml index 827946a43dc..2024d677205 100644 --- a/.github/workflows/docker_build_images.yml +++ b/.github/workflows/docker_build_images.yml @@ -16,7 +16,7 @@ permissions: read-all jobs: build_and_push_vttestserver: name: Build and push vttestserver - runs-on: gh-hosted-runners-16cores-1 + runs-on: gh-hosted-runners-16cores-1-24.04 if: github.repository == 'vitessio/vitess' strategy: @@ -70,7 +70,7 @@ jobs: build_and_push_lite: name: Build and push lite - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 if: github.repository == 'vitessio/vitess' strategy: @@ -132,7 +132,7 @@ jobs: build_and_push_components: name: Build and push - runs-on: gh-hosted-runners-16cores-1 + runs-on: gh-hosted-runners-16cores-1-24.04 if: github.repository == 'vitessio/vitess' && needs.build_and_push_lite.result == 'success' needs: - build_and_push_lite @@ -226,7 +226,7 @@ jobs: slack_notification: name: Slack Notification if failed - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 needs: - build_and_push_vttestserver - build_and_push_lite diff --git a/.github/workflows/docker_test_cluster_10.yml b/.github/workflows/docker_test_cluster_10.yml index ce1c9e7277b..10a7c1551c0 100644 --- a/.github/workflows/docker_test_cluster_10.yml +++ b/.github/workflows/docker_test_cluster_10.yml @@ -5,7 +5,7 @@ jobs: build: name: Docker Test Cluster 10 - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI diff --git a/.github/workflows/docker_test_cluster_25.yml b/.github/workflows/docker_test_cluster_25.yml index eb2319b8c9f..34463ce3830 100644 --- a/.github/workflows/docker_test_cluster_25.yml +++ b/.github/workflows/docker_test_cluster_25.yml @@ -5,7 +5,7 @@ jobs: build: name: Docker Test Cluster 25 - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI diff --git a/.github/workflows/e2e_race.yml b/.github/workflows/e2e_race.yml index 78c8d0b1753..50e1e4b9485 100644 --- a/.github/workflows/e2e_race.yml +++ b/.github/workflows/e2e_race.yml @@ -5,7 +5,7 @@ jobs: build: name: End-to-End Test (Race) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI run: | @@ -65,13 +65,13 @@ jobs: # 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 + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 install -y mysql-server mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils + sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils sudo service mysql stop sudo service etcd stop sudo ln -s /etc/apparmor.d/usr.sbin.mysqld /etc/apparmor.d/disable/ diff --git a/.github/workflows/endtoend.yml b/.github/workflows/endtoend.yml index 9e06e4ee44b..f061e8bee68 100644 --- a/.github/workflows/endtoend.yml +++ b/.github/workflows/endtoend.yml @@ -5,7 +5,7 @@ jobs: build: name: End-to-End Test - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI run: | @@ -63,7 +63,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' run: | sudo apt-get update - sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd curl git wget + sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd-client etcd-server curl git wget sudo service mysql stop sudo service etcd stop sudo ln -s /etc/apparmor.d/usr.sbin.mysqld /etc/apparmor.d/disable/ diff --git a/.github/workflows/local_example.yml b/.github/workflows/local_example.yml index 65669158415..fc9724aa6ac 100644 --- a/.github/workflows/local_example.yml +++ b/.github/workflows/local_example.yml @@ -4,8 +4,8 @@ permissions: read-all jobs: build: - name: Local example using ${{ matrix.topo }} on ubuntu-22.04 - runs-on: gh-hosted-runners-16cores-1 + name: Local example using ${{ matrix.topo }} on Ubuntu + runs-on: gh-hosted-runners-16cores-1-24.04 strategy: matrix: topo: [consul,etcd,zk2] @@ -67,24 +67,6 @@ jobs: - name: Get dependencies if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.examples == 'true' run: | - if [ ${{matrix.os}} = "ubuntu-22.04" ]; then - # 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 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 - 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 - elif [ ${{matrix.os}} = "macos-latest" ]; then - brew install mysql@5.7 make unzip etcd curl git wget - fi go mod download - name: Run make minimaltools diff --git a/.github/workflows/region_example.yml b/.github/workflows/region_example.yml index c031ea315b5..c17f43eec11 100644 --- a/.github/workflows/region_example.yml +++ b/.github/workflows/region_example.yml @@ -4,8 +4,8 @@ permissions: read-all jobs: build: - name: Region Sharding example using ${{ matrix.topo }} on ubuntu-22.04 - runs-on: gh-hosted-runners-16cores-1 + name: Region Sharding example using ${{ matrix.topo }} on Ubuntu + runs-on: gh-hosted-runners-16cores-1-24.04 strategy: matrix: topo: [etcd] @@ -67,24 +67,6 @@ jobs: - name: Get dependencies if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.examples == 'true' run: | - if [ ${{matrix.os}} = "ubuntu-22.04" ]; then - # 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 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 - 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 - elif [ ${{matrix.os}} = "macos-latest" ]; then - brew install mysql@5.7 make unzip etcd curl git wget - fi go mod download - name: Run make minimaltools diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml index 7d20694b850..b879ec8c144 100644 --- a/.github/workflows/scorecards.yml +++ b/.github/workflows/scorecards.yml @@ -14,7 +14,7 @@ permissions: read-all jobs: analysis: name: Scorecard analysis - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 permissions: # Needed to upload the results to code-scanning dashboard. security-events: write diff --git a/.github/workflows/static_checks_etc.yml b/.github/workflows/static_checks_etc.yml index cd896d5fd29..cd2cce93827 100644 --- a/.github/workflows/static_checks_etc.yml +++ b/.github/workflows/static_checks_etc.yml @@ -9,7 +9,7 @@ permissions: read-all jobs: build: name: Static Code Checks Etc - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -152,7 +152,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && (steps.changes.outputs.parser_changes == 'true' || steps.changes.outputs.go_files == 'true') run: | sudo apt-get update - sudo apt-get install -y make unzip g++ etcd curl git wget + sudo apt-get install -y make unzip g++ etcd-client etcd-server curl git wget sudo service etcd stop go mod download diff --git a/.github/workflows/unit_race.yml b/.github/workflows/unit_race.yml index e764cd33d9d..8ffb74bd2cb 100644 --- a/.github/workflows/unit_race.yml +++ b/.github/workflows/unit_race.yml @@ -15,7 +15,7 @@ jobs: build: name: Unit Test (Race) - runs-on: gh-hosted-runners-16cores-1 + runs-on: gh-hosted-runners-16cores-1-24.04 steps: - name: Skip CI run: | diff --git a/.github/workflows/unit_race_evalengine.yml b/.github/workflows/unit_race_evalengine.yml index 5a5cb637681..d6d248f066e 100644 --- a/.github/workflows/unit_race_evalengine.yml +++ b/.github/workflows/unit_race_evalengine.yml @@ -15,7 +15,7 @@ jobs: build: name: Unit Test (Evalengine_Race) - runs-on: gh-hosted-runners-16cores-1 + runs-on: gh-hosted-runners-16cores-1-24.04 steps: - name: Skip CI run: | diff --git a/.github/workflows/unit_test_evalengine_mysql57.yml b/.github/workflows/unit_test_evalengine_mysql57.yml index 09cb392c794..ecc366b38fe 100644 --- a/.github/workflows/unit_test_evalengine_mysql57.yml +++ b/.github/workflows/unit_test_evalengine_mysql57.yml @@ -16,7 +16,7 @@ env: jobs: test: name: Unit Test (evalengine_mysql57) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -89,30 +89,37 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.unit_tests == 'true' run: | export DEBIAN_FRONTEND="noninteractive" - sudo apt-get -qq update + sudo apt-get update # Uninstall any previously installed MySQL first sudo systemctl stop apparmor - sudo DEBIAN_FRONTEND="noninteractive" apt-get -qq remove -y --purge mysql-server mysql-client mysql-common - sudo apt-get -qq -y autoremove - sudo apt-get -qq -y autoclean + sudo DEBIAN_FRONTEND="noninteractive" apt-get remove -y --purge mysql-server mysql-client mysql-common + sudo apt-get -y autoremove + sudo apt-get -y autoclean sudo deluser mysql sudo rm -rf /var/lib/mysql sudo rm -rf /etc/mysql # Get key to latest MySQL repo sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-1_all.deb # Bionic packages are still compatible for Jammy since there's no MySQL 5.7 # packages for Jammy. echo mysql-apt-config mysql-apt-config/repo-codename select bionic | sudo debconf-set-selections echo mysql-apt-config mysql-apt-config/select-server select mysql-5.7 | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* - sudo apt-get -qq update - sudo DEBIAN_FRONTEND="noninteractive" apt-get -qq install -y mysql-client=5.7* mysql-community-server=5.7* mysql-server=5.7* libncurses6 + sudo apt-get update + # We have to install this old version of libaio1. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo DEBIAN_FRONTEND="noninteractive" apt-get install -y mysql-client=5.7* mysql-community-server=5.7* mysql-server=5.7* libncurses6 - sudo apt-get -qq install -y make unzip g++ curl git wget ant openjdk-11-jdk eatmydata + sudo apt-get install -y make unzip g++ curl git wget ant openjdk-11-jdk eatmydata sudo service mysql stop sudo bash -c "echo '/usr/sbin/mysqld { }' > /etc/apparmor.d/usr.sbin.mysqld" # https://bugs.launchpad.net/ubuntu/+source/mariadb-10.1/+bug/1806263 sudo ln -s /etc/apparmor.d/usr.sbin.mysqld /etc/apparmor.d/disable/ diff --git a/.github/workflows/unit_test_evalengine_mysql80.yml b/.github/workflows/unit_test_evalengine_mysql80.yml index 5578829d48d..e6e802b52d8 100644 --- a/.github/workflows/unit_test_evalengine_mysql80.yml +++ b/.github/workflows/unit_test_evalengine_mysql80.yml @@ -16,7 +16,7 @@ env: jobs: test: name: Unit Test (evalengine_mysql80) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -89,27 +89,27 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.unit_tests == 'true' run: | export DEBIAN_FRONTEND="noninteractive" - sudo apt-get -qq update + sudo apt-get update # Uninstall any previously installed MySQL first sudo systemctl stop apparmor - sudo DEBIAN_FRONTEND="noninteractive" apt-get -qq remove -y --purge mysql-server mysql-client mysql-common - sudo apt-get -qq -y autoremove - sudo apt-get -qq -y autoclean + sudo DEBIAN_FRONTEND="noninteractive" apt-get remove -y --purge mysql-server mysql-client mysql-common + sudo apt-get -y autoremove + sudo apt-get -y autoclean sudo deluser mysql sudo rm -rf /var/lib/mysql sudo rm -rf /etc/mysql # Get key to latest MySQL repo sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 - sudo DEBIAN_FRONTEND="noninteractive" apt-get -qq install -y mysql-server mysql-client + sudo apt-get update + sudo DEBIAN_FRONTEND="noninteractive" apt-get install -y mysql-server mysql-client - sudo apt-get -qq install -y make unzip g++ curl git wget ant openjdk-11-jdk eatmydata + sudo apt-get install -y make unzip g++ curl git wget ant openjdk-11-jdk eatmydata sudo service mysql stop sudo bash -c "echo '/usr/sbin/mysqld { }' > /etc/apparmor.d/usr.sbin.mysqld" # https://bugs.launchpad.net/ubuntu/+source/mariadb-10.1/+bug/1806263 sudo ln -s /etc/apparmor.d/usr.sbin.mysqld /etc/apparmor.d/disable/ diff --git a/.github/workflows/unit_test_evalengine_mysql84.yml b/.github/workflows/unit_test_evalengine_mysql84.yml index 2a86d5bfabd..46736dac349 100644 --- a/.github/workflows/unit_test_evalengine_mysql84.yml +++ b/.github/workflows/unit_test_evalengine_mysql84.yml @@ -16,7 +16,7 @@ env: jobs: test: name: Unit Test (evalengine_mysql84) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -89,27 +89,27 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.unit_tests == 'true' run: | export DEBIAN_FRONTEND="noninteractive" - sudo apt-get -qq update + sudo apt-get update # Uninstall any previously installed MySQL first sudo systemctl stop apparmor - sudo DEBIAN_FRONTEND="noninteractive" apt-get -qq remove -y --purge mysql-server mysql-client mysql-common - sudo apt-get -qq -y autoremove - sudo apt-get -qq -y autoclean + sudo DEBIAN_FRONTEND="noninteractive" apt-get remove -y --purge mysql-server mysql-client mysql-common + sudo apt-get -y autoremove + sudo apt-get -y autoclean sudo deluser mysql sudo rm -rf /var/lib/mysql sudo rm -rf /etc/mysql # Get key to latest MySQL repo sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-1_all.deb echo mysql-apt-config mysql-apt-config/select-server select mysql-8.4-lts | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* - sudo apt-get -qq update - sudo DEBIAN_FRONTEND="noninteractive" apt-get -qq install -y mysql-server mysql-client + sudo apt-get update + sudo DEBIAN_FRONTEND="noninteractive" apt-get install -y mysql-server mysql-client - sudo apt-get -qq install -y make unzip g++ curl git wget ant openjdk-11-jdk eatmydata + sudo apt-get install -y make unzip g++ curl git wget ant openjdk-11-jdk eatmydata sudo service mysql stop sudo bash -c "echo '/usr/sbin/mysqld { }' > /etc/apparmor.d/usr.sbin.mysqld" # https://bugs.launchpad.net/ubuntu/+source/mariadb-10.1/+bug/1806263 sudo ln -s /etc/apparmor.d/usr.sbin.mysqld /etc/apparmor.d/disable/ diff --git a/.github/workflows/unit_test_mysql57.yml b/.github/workflows/unit_test_mysql57.yml index bd4b6ed8c3c..3eaf02d1538 100644 --- a/.github/workflows/unit_test_mysql57.yml +++ b/.github/workflows/unit_test_mysql57.yml @@ -16,7 +16,7 @@ env: jobs: test: name: Unit Test (mysql57) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -89,30 +89,37 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.unit_tests == 'true' run: | export DEBIAN_FRONTEND="noninteractive" - sudo apt-get -qq update + sudo apt-get update # Uninstall any previously installed MySQL first sudo systemctl stop apparmor - sudo DEBIAN_FRONTEND="noninteractive" apt-get -qq remove -y --purge mysql-server mysql-client mysql-common - sudo apt-get -qq -y autoremove - sudo apt-get -qq -y autoclean + sudo DEBIAN_FRONTEND="noninteractive" apt-get remove -y --purge mysql-server mysql-client mysql-common + sudo apt-get -y autoremove + sudo apt-get -y autoclean sudo deluser mysql sudo rm -rf /var/lib/mysql sudo rm -rf /etc/mysql # Get key to latest MySQL repo sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-1_all.deb # Bionic packages are still compatible for Jammy since there's no MySQL 5.7 # packages for Jammy. echo mysql-apt-config mysql-apt-config/repo-codename select bionic | sudo debconf-set-selections echo mysql-apt-config mysql-apt-config/select-server select mysql-5.7 | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* - sudo apt-get -qq update - sudo DEBIAN_FRONTEND="noninteractive" apt-get -qq install -y mysql-client=5.7* mysql-community-server=5.7* mysql-server=5.7* libncurses6 + sudo apt-get update + # We have to install this old version of libaio1. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo DEBIAN_FRONTEND="noninteractive" apt-get install -y mysql-client=5.7* mysql-community-server=5.7* mysql-server=5.7* libncurses6 - sudo apt-get -qq install -y make unzip g++ curl git wget ant openjdk-11-jdk eatmydata + sudo apt-get install -y make unzip g++ curl git wget ant openjdk-11-jdk eatmydata sudo service mysql stop sudo bash -c "echo '/usr/sbin/mysqld { }' > /etc/apparmor.d/usr.sbin.mysqld" # https://bugs.launchpad.net/ubuntu/+source/mariadb-10.1/+bug/1806263 sudo ln -s /etc/apparmor.d/usr.sbin.mysqld /etc/apparmor.d/disable/ diff --git a/.github/workflows/unit_test_mysql80.yml b/.github/workflows/unit_test_mysql80.yml index 03f31a25cc8..c036e6dd477 100644 --- a/.github/workflows/unit_test_mysql80.yml +++ b/.github/workflows/unit_test_mysql80.yml @@ -16,7 +16,7 @@ env: jobs: test: name: Unit Test (mysql80) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -89,27 +89,27 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.unit_tests == 'true' run: | export DEBIAN_FRONTEND="noninteractive" - sudo apt-get -qq update + sudo apt-get update # Uninstall any previously installed MySQL first sudo systemctl stop apparmor - sudo DEBIAN_FRONTEND="noninteractive" apt-get -qq remove -y --purge mysql-server mysql-client mysql-common - sudo apt-get -qq -y autoremove - sudo apt-get -qq -y autoclean + sudo DEBIAN_FRONTEND="noninteractive" apt-get remove -y --purge mysql-server mysql-client mysql-common + sudo apt-get -y autoremove + sudo apt-get -y autoclean sudo deluser mysql sudo rm -rf /var/lib/mysql sudo rm -rf /etc/mysql # Get key to latest MySQL repo sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 - sudo DEBIAN_FRONTEND="noninteractive" apt-get -qq install -y mysql-server mysql-client + sudo apt-get update + sudo DEBIAN_FRONTEND="noninteractive" apt-get install -y mysql-server mysql-client - sudo apt-get -qq install -y make unzip g++ curl git wget ant openjdk-11-jdk eatmydata + sudo apt-get install -y make unzip g++ curl git wget ant openjdk-11-jdk eatmydata sudo service mysql stop sudo bash -c "echo '/usr/sbin/mysqld { }' > /etc/apparmor.d/usr.sbin.mysqld" # https://bugs.launchpad.net/ubuntu/+source/mariadb-10.1/+bug/1806263 sudo ln -s /etc/apparmor.d/usr.sbin.mysqld /etc/apparmor.d/disable/ diff --git a/.github/workflows/unit_test_mysql84.yml b/.github/workflows/unit_test_mysql84.yml index 95a56aeff81..84447ce390b 100644 --- a/.github/workflows/unit_test_mysql84.yml +++ b/.github/workflows/unit_test_mysql84.yml @@ -16,7 +16,7 @@ env: jobs: test: name: Unit Test (mysql84) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -89,27 +89,27 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.unit_tests == 'true' run: | export DEBIAN_FRONTEND="noninteractive" - sudo apt-get -qq update + sudo apt-get update # Uninstall any previously installed MySQL first sudo systemctl stop apparmor - sudo DEBIAN_FRONTEND="noninteractive" apt-get -qq remove -y --purge mysql-server mysql-client mysql-common - sudo apt-get -qq -y autoremove - sudo apt-get -qq -y autoclean + sudo DEBIAN_FRONTEND="noninteractive" apt-get remove -y --purge mysql-server mysql-client mysql-common + sudo apt-get -y autoremove + sudo apt-get -y autoclean sudo deluser mysql sudo rm -rf /var/lib/mysql sudo rm -rf /etc/mysql # Get key to latest MySQL repo sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-1_all.deb echo mysql-apt-config mysql-apt-config/select-server select mysql-8.4-lts | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* - sudo apt-get -qq update - sudo DEBIAN_FRONTEND="noninteractive" apt-get -qq install -y mysql-server mysql-client + sudo apt-get update + sudo DEBIAN_FRONTEND="noninteractive" apt-get install -y mysql-server mysql-client - sudo apt-get -qq install -y make unzip g++ curl git wget ant openjdk-11-jdk eatmydata + sudo apt-get install -y make unzip g++ curl git wget ant openjdk-11-jdk eatmydata sudo service mysql stop sudo bash -c "echo '/usr/sbin/mysqld { }' > /etc/apparmor.d/usr.sbin.mysqld" # https://bugs.launchpad.net/ubuntu/+source/mariadb-10.1/+bug/1806263 sudo ln -s /etc/apparmor.d/usr.sbin.mysqld /etc/apparmor.d/disable/ diff --git a/.github/workflows/update_golang_dependencies.yml b/.github/workflows/update_golang_dependencies.yml index a614a634d83..33c5157097c 100644 --- a/.github/workflows/update_golang_dependencies.yml +++ b/.github/workflows/update_golang_dependencies.yml @@ -14,7 +14,7 @@ jobs: contents: write pull-requests: write name: Update Golang Dependencies - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Set up Go uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 diff --git a/.github/workflows/update_golang_version.yml b/.github/workflows/update_golang_version.yml index 503cad50ce8..09bbf451451 100644 --- a/.github/workflows/update_golang_version.yml +++ b/.github/workflows/update_golang_version.yml @@ -17,7 +17,7 @@ jobs: matrix: branch: [ main, release-21.0, release-20.0, release-19.0 ] name: Update Golang Version - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Check out code uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 diff --git a/.github/workflows/upgrade_downgrade_test_backups_e2e.yml b/.github/workflows/upgrade_downgrade_test_backups_e2e.yml index f1c4f335f0d..a56aad2f523 100644 --- a/.github/workflows/upgrade_downgrade_test_backups_e2e.yml +++ b/.github/workflows/upgrade_downgrade_test_backups_e2e.yml @@ -13,7 +13,7 @@ jobs: upgrade_downgrade_test_e2e: timeout-minutes: 60 name: Run Upgrade Downgrade Test - Backups - E2E - runs-on: gh-hosted-runners-16cores-1 + runs-on: gh-hosted-runners-16cores-1-24.04 steps: - name: Skip CI @@ -87,7 +87,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' run: | sudo apt-get update - sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd curl git wget eatmydata + sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata sudo service mysql stop sudo service etcd stop sudo ln -s /etc/apparmor.d/usr.sbin.mysqld /etc/apparmor.d/disable/ diff --git a/.github/workflows/upgrade_downgrade_test_backups_e2e_next_release.yml b/.github/workflows/upgrade_downgrade_test_backups_e2e_next_release.yml index 73c24c5e72e..7ee6c2db799 100644 --- a/.github/workflows/upgrade_downgrade_test_backups_e2e_next_release.yml +++ b/.github/workflows/upgrade_downgrade_test_backups_e2e_next_release.yml @@ -14,7 +14,7 @@ jobs: upgrade_downgrade_test_e2e: timeout-minutes: 60 name: Run Upgrade Downgrade Test - Backups - E2E - Next Release - runs-on: gh-hosted-runners-16cores-1 + runs-on: gh-hosted-runners-16cores-1-24.04 steps: - name: Skip CI @@ -89,7 +89,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' run: | sudo apt-get update - sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd curl git wget eatmydata + sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata sudo service mysql stop sudo service etcd stop sudo ln -s /etc/apparmor.d/usr.sbin.mysqld /etc/apparmor.d/disable/ diff --git a/.github/workflows/upgrade_downgrade_test_backups_manual.yml b/.github/workflows/upgrade_downgrade_test_backups_manual.yml index 9c866affb0b..00aab1b78ff 100644 --- a/.github/workflows/upgrade_downgrade_test_backups_manual.yml +++ b/.github/workflows/upgrade_downgrade_test_backups_manual.yml @@ -15,7 +15,7 @@ jobs: upgrade_downgrade_test_manual: timeout-minutes: 40 name: Run Upgrade Downgrade Test - Backups - Manual - runs-on: gh-hosted-runners-16cores-1 + runs-on: gh-hosted-runners-16cores-1-24.04 steps: - name: Skip CI @@ -102,14 +102,14 @@ jobs: # Install MySQL 8.0 sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 sudo DEBIAN_FRONTEND="noninteractive" apt-get install -y mysql-server mysql-client # Install everything else we need, and configure - sudo apt-get install -y make unzip g++ etcd curl git wget eatmydata grep + sudo apt-get install -y make unzip g++ etcd-client etcd-server curl git wget eatmydata grep sudo service mysql stop sudo service etcd stop sudo bash -c "echo '/usr/sbin/mysqld { }' > /etc/apparmor.d/usr.sbin.mysqld" # https://bugs.launchpad.net/ubuntu/+source/mariadb-10.1/+bug/1806263 diff --git a/.github/workflows/upgrade_downgrade_test_backups_manual_next_release.yml b/.github/workflows/upgrade_downgrade_test_backups_manual_next_release.yml index 802ee78d940..768268271a1 100644 --- a/.github/workflows/upgrade_downgrade_test_backups_manual_next_release.yml +++ b/.github/workflows/upgrade_downgrade_test_backups_manual_next_release.yml @@ -15,7 +15,7 @@ jobs: upgrade_downgrade_test_manual: timeout-minutes: 40 name: Run Upgrade Downgrade Test - Backups - Manual - Next Release - runs-on: gh-hosted-runners-16cores-1 + runs-on: gh-hosted-runners-16cores-1-24.04 steps: - name: Skip CI @@ -103,14 +103,14 @@ jobs: # Install MySQL 8.0 sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 sudo DEBIAN_FRONTEND="noninteractive" apt-get install -y mysql-server mysql-client # Install everything else we need, and configure - sudo apt-get install -y make unzip g++ etcd curl git wget eatmydata grep + sudo apt-get install -y make unzip g++ etcd-client etcd-server curl git wget eatmydata grep sudo service mysql stop sudo service etcd stop sudo bash -c "echo '/usr/sbin/mysqld { }' > /etc/apparmor.d/usr.sbin.mysqld" # https://bugs.launchpad.net/ubuntu/+source/mariadb-10.1/+bug/1806263 diff --git a/.github/workflows/upgrade_downgrade_test_onlineddl_flow.yml b/.github/workflows/upgrade_downgrade_test_onlineddl_flow.yml index 89e5f7c4d25..72426e70a61 100644 --- a/.github/workflows/upgrade_downgrade_test_onlineddl_flow.yml +++ b/.github/workflows/upgrade_downgrade_test_onlineddl_flow.yml @@ -16,7 +16,7 @@ jobs: upgrade_downgrade_test: name: Run Upgrade Downgrade Test - Online DDL flow - runs-on: gh-hosted-runners-16cores-1 + runs-on: gh-hosted-runners-16cores-1-24.04 steps: - name: Skip CI @@ -108,13 +108,13 @@ jobs: sudo rm -rf /etc/mysql # Install mysql80 sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 sudo DEBIAN_FRONTEND="noninteractive" apt-get install -y mysql-server mysql-client # Install everything else we need, and configure - sudo apt-get install -y make unzip g++ etcd curl git wget eatmydata + sudo apt-get install -y make unzip g++ etcd-client etcd-server curl git wget eatmydata sudo service mysql stop sudo service etcd stop sudo bash -c "echo '/usr/sbin/mysqld { }' > /etc/apparmor.d/usr.sbin.mysqld" # https://bugs.launchpad.net/ubuntu/+source/mariadb-10.1/+bug/1806263 diff --git a/.github/workflows/upgrade_downgrade_test_query_serving_queries.yml b/.github/workflows/upgrade_downgrade_test_query_serving_queries.yml index f7d68dfee04..5ac1a55334c 100644 --- a/.github/workflows/upgrade_downgrade_test_query_serving_queries.yml +++ b/.github/workflows/upgrade_downgrade_test_query_serving_queries.yml @@ -16,7 +16,7 @@ jobs: upgrade_downgrade_test: name: Run Upgrade Downgrade Test - Query Serving (Queries) - runs-on: gh-hosted-runners-16cores-1 + runs-on: gh-hosted-runners-16cores-1-24.04 steps: - name: Skip CI @@ -100,13 +100,13 @@ jobs: sudo rm -rf /etc/mysql # Install mysql80 sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 sudo DEBIAN_FRONTEND="noninteractive" apt-get install -y mysql-server mysql-client # Install everything else we need, and configure - sudo apt-get install -y make unzip g++ etcd curl git wget eatmydata + sudo apt-get install -y make unzip g++ etcd-client etcd-server curl git wget eatmydata sudo service mysql stop sudo service etcd stop sudo bash -c "echo '/usr/sbin/mysqld { }' > /etc/apparmor.d/usr.sbin.mysqld" # https://bugs.launchpad.net/ubuntu/+source/mariadb-10.1/+bug/1806263 diff --git a/.github/workflows/upgrade_downgrade_test_query_serving_queries_next_release.yml b/.github/workflows/upgrade_downgrade_test_query_serving_queries_next_release.yml index e3a34bd43f8..c65fff08c52 100644 --- a/.github/workflows/upgrade_downgrade_test_query_serving_queries_next_release.yml +++ b/.github/workflows/upgrade_downgrade_test_query_serving_queries_next_release.yml @@ -16,7 +16,7 @@ jobs: upgrade_downgrade_test: name: Run Upgrade Downgrade Test - Query Serving (Queries) Next Release - runs-on: gh-hosted-runners-16cores-1 + runs-on: gh-hosted-runners-16cores-1-24.04 steps: - name: Skip CI @@ -101,13 +101,13 @@ jobs: sudo rm -rf /etc/mysql # Install mysql80 sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 sudo DEBIAN_FRONTEND="noninteractive" apt-get install -y mysql-server mysql-client # Install everything else we need, and configure - sudo apt-get install -y make unzip g++ etcd curl git wget eatmydata + sudo apt-get install -y make unzip g++ etcd-client etcd-server curl git wget eatmydata sudo service mysql stop sudo service etcd stop sudo bash -c "echo '/usr/sbin/mysqld { }' > /etc/apparmor.d/usr.sbin.mysqld" # https://bugs.launchpad.net/ubuntu/+source/mariadb-10.1/+bug/1806263 diff --git a/.github/workflows/upgrade_downgrade_test_query_serving_schema.yml b/.github/workflows/upgrade_downgrade_test_query_serving_schema.yml index a9162d2f199..4b5fad0ab29 100644 --- a/.github/workflows/upgrade_downgrade_test_query_serving_schema.yml +++ b/.github/workflows/upgrade_downgrade_test_query_serving_schema.yml @@ -16,7 +16,7 @@ jobs: upgrade_downgrade_test: name: Run Upgrade Downgrade Test - Query Serving (Schema) - runs-on: gh-hosted-runners-16cores-1 + runs-on: gh-hosted-runners-16cores-1-24.04 steps: - name: Skip CI @@ -100,13 +100,13 @@ jobs: sudo rm -rf /etc/mysql # Install mysql80 sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 sudo DEBIAN_FRONTEND="noninteractive" apt-get install -y mysql-server mysql-client # Install everything else we need, and configure - sudo apt-get install -y make unzip g++ etcd curl git wget eatmydata + sudo apt-get install -y make unzip g++ etcd-client etcd-server curl git wget eatmydata sudo service mysql stop sudo service etcd stop sudo bash -c "echo '/usr/sbin/mysqld { }' > /etc/apparmor.d/usr.sbin.mysqld" # https://bugs.launchpad.net/ubuntu/+source/mariadb-10.1/+bug/1806263 diff --git a/.github/workflows/upgrade_downgrade_test_query_serving_schema_next_release.yml b/.github/workflows/upgrade_downgrade_test_query_serving_schema_next_release.yml index bf5f63d8836..6157079db6f 100644 --- a/.github/workflows/upgrade_downgrade_test_query_serving_schema_next_release.yml +++ b/.github/workflows/upgrade_downgrade_test_query_serving_schema_next_release.yml @@ -16,7 +16,7 @@ jobs: upgrade_downgrade_test: name: Run Upgrade Downgrade Test - Query Serving (Schema) Next Release - runs-on: gh-hosted-runners-16cores-1 + runs-on: gh-hosted-runners-16cores-1-24.04 steps: - name: Skip CI @@ -101,13 +101,13 @@ jobs: sudo rm -rf /etc/mysql # Install mysql80 sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 sudo DEBIAN_FRONTEND="noninteractive" apt-get install -y mysql-server mysql-client # Install everything else we need, and configure - sudo apt-get install -y make unzip g++ etcd curl git wget eatmydata + sudo apt-get install -y make unzip g++ etcd-client etcd-server curl git wget eatmydata sudo service mysql stop sudo service etcd stop sudo bash -c "echo '/usr/sbin/mysqld { }' > /etc/apparmor.d/usr.sbin.mysqld" # https://bugs.launchpad.net/ubuntu/+source/mariadb-10.1/+bug/1806263 diff --git a/.github/workflows/upgrade_downgrade_test_reparent_new_vtctl.yml b/.github/workflows/upgrade_downgrade_test_reparent_new_vtctl.yml index ac6615b5788..931d9174c31 100644 --- a/.github/workflows/upgrade_downgrade_test_reparent_new_vtctl.yml +++ b/.github/workflows/upgrade_downgrade_test_reparent_new_vtctl.yml @@ -16,7 +16,7 @@ jobs: upgrade_downgrade_test: name: Run Upgrade Downgrade Test - Reparent New Vtctl - runs-on: gh-hosted-runners-16cores-1 + runs-on: gh-hosted-runners-16cores-1-24.04 steps: - name: Skip CI @@ -101,13 +101,13 @@ jobs: sudo rm -rf /etc/mysql # Install mysql80 sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 sudo DEBIAN_FRONTEND="noninteractive" apt-get install -y mysql-server mysql-client # Install everything else we need, and configure - sudo apt-get install -y make unzip g++ etcd curl git wget eatmydata + sudo apt-get install -y make unzip g++ etcd-client etcd-server curl git wget eatmydata sudo service mysql stop sudo service etcd stop sudo bash -c "echo '/usr/sbin/mysqld { }' > /etc/apparmor.d/usr.sbin.mysqld" # https://bugs.launchpad.net/ubuntu/+source/mariadb-10.1/+bug/1806263 diff --git a/.github/workflows/upgrade_downgrade_test_reparent_new_vttablet.yml b/.github/workflows/upgrade_downgrade_test_reparent_new_vttablet.yml index e163ab35ea4..d77b7be53b3 100644 --- a/.github/workflows/upgrade_downgrade_test_reparent_new_vttablet.yml +++ b/.github/workflows/upgrade_downgrade_test_reparent_new_vttablet.yml @@ -16,7 +16,7 @@ jobs: upgrade_downgrade_test: name: Run Upgrade Downgrade Test - Reparent New VTTablet - runs-on: gh-hosted-runners-16cores-1 + runs-on: gh-hosted-runners-16cores-1-24.04 steps: - name: Skip CI @@ -101,13 +101,13 @@ jobs: sudo rm -rf /etc/mysql # Install mysql80 sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 sudo DEBIAN_FRONTEND="noninteractive" apt-get install -y mysql-server mysql-client # Install everything else we need, and configure - sudo apt-get install -y make unzip g++ etcd curl git wget eatmydata + sudo apt-get install -y make unzip g++ etcd-client etcd-server curl git wget eatmydata sudo service mysql stop sudo service etcd stop sudo bash -c "echo '/usr/sbin/mysqld { }' > /etc/apparmor.d/usr.sbin.mysqld" # https://bugs.launchpad.net/ubuntu/+source/mariadb-10.1/+bug/1806263 diff --git a/.github/workflows/upgrade_downgrade_test_reparent_old_vtctl.yml b/.github/workflows/upgrade_downgrade_test_reparent_old_vtctl.yml index 93e94466b74..701025d7ecc 100644 --- a/.github/workflows/upgrade_downgrade_test_reparent_old_vtctl.yml +++ b/.github/workflows/upgrade_downgrade_test_reparent_old_vtctl.yml @@ -16,7 +16,7 @@ jobs: upgrade_downgrade_test: name: Run Upgrade Downgrade Test - Reparent Old Vtctl - runs-on: gh-hosted-runners-16cores-1 + runs-on: gh-hosted-runners-16cores-1-24.04 steps: - name: Skip CI @@ -100,13 +100,13 @@ jobs: sudo rm -rf /etc/mysql # Install mysql80 sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 sudo DEBIAN_FRONTEND="noninteractive" apt-get install -y mysql-server mysql-client # Install everything else we need, and configure - sudo apt-get install -y make unzip g++ etcd curl git wget eatmydata + sudo apt-get install -y make unzip g++ etcd-client etcd-server curl git wget eatmydata sudo service mysql stop sudo service etcd stop sudo bash -c "echo '/usr/sbin/mysqld { }' > /etc/apparmor.d/usr.sbin.mysqld" # https://bugs.launchpad.net/ubuntu/+source/mariadb-10.1/+bug/1806263 diff --git a/.github/workflows/upgrade_downgrade_test_reparent_old_vttablet.yml b/.github/workflows/upgrade_downgrade_test_reparent_old_vttablet.yml index 12f6b514aa1..8b121d4af10 100644 --- a/.github/workflows/upgrade_downgrade_test_reparent_old_vttablet.yml +++ b/.github/workflows/upgrade_downgrade_test_reparent_old_vttablet.yml @@ -16,7 +16,7 @@ jobs: upgrade_downgrade_test: name: Run Upgrade Downgrade Test - Reparent Old VTTablet - runs-on: gh-hosted-runners-16cores-1 + runs-on: gh-hosted-runners-16cores-1-24.04 steps: - name: Skip CI @@ -100,13 +100,13 @@ jobs: sudo rm -rf /etc/mysql # Install mysql80 sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 sudo DEBIAN_FRONTEND="noninteractive" apt-get install -y mysql-server mysql-client # Install everything else we need, and configure - sudo apt-get install -y make unzip g++ etcd curl git wget eatmydata + sudo apt-get install -y make unzip g++ etcd-client etcd-server curl git wget eatmydata sudo service mysql stop sudo service etcd stop sudo bash -c "echo '/usr/sbin/mysqld { }' > /etc/apparmor.d/usr.sbin.mysqld" # https://bugs.launchpad.net/ubuntu/+source/mariadb-10.1/+bug/1806263 diff --git a/.github/workflows/upgrade_downgrade_test_semi_sync.yml b/.github/workflows/upgrade_downgrade_test_semi_sync.yml index 16dac6e7fd2..f12e323654e 100644 --- a/.github/workflows/upgrade_downgrade_test_semi_sync.yml +++ b/.github/workflows/upgrade_downgrade_test_semi_sync.yml @@ -13,7 +13,7 @@ jobs: upgrade_downgrade_test_e2e: timeout-minutes: 60 name: Run Semi Sync Upgrade Downgrade Test - runs-on: gh-hosted-runners-16cores-1 + runs-on: gh-hosted-runners-16cores-1-24.04 steps: - name: Skip CI @@ -87,7 +87,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' run: | sudo apt-get update - sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd curl git wget eatmydata + sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata sudo service mysql stop sudo service etcd stop sudo ln -s /etc/apparmor.d/usr.sbin.mysqld /etc/apparmor.d/disable/ diff --git a/.github/workflows/vitess_tester_vtgate.yml b/.github/workflows/vitess_tester_vtgate.yml index 5f8c52d454b..7d5cf89a3ec 100644 --- a/.github/workflows/vitess_tester_vtgate.yml +++ b/.github/workflows/vitess_tester_vtgate.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Vitess Tester (vtgate) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -95,7 +95,7 @@ jobs: # 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 + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 diff --git a/.github/workflows/vtadmin_web_build.yml b/.github/workflows/vtadmin_web_build.yml index 096b70cd002..822fa8a0920 100644 --- a/.github/workflows/vtadmin_web_build.yml +++ b/.github/workflows/vtadmin_web_build.yml @@ -16,7 +16,7 @@ permissions: read-all jobs: build: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI run: | diff --git a/.github/workflows/vtadmin_web_lint.yml b/.github/workflows/vtadmin_web_lint.yml index 5435b4b54fe..694c24734a6 100644 --- a/.github/workflows/vtadmin_web_lint.yml +++ b/.github/workflows/vtadmin_web_lint.yml @@ -16,7 +16,7 @@ permissions: read-all jobs: lint: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI run: | diff --git a/.github/workflows/vtadmin_web_unit_tests.yml b/.github/workflows/vtadmin_web_unit_tests.yml index b765a68fa76..72a887c1926 100644 --- a/.github/workflows/vtadmin_web_unit_tests.yml +++ b/.github/workflows/vtadmin_web_unit_tests.yml @@ -16,7 +16,7 @@ permissions: read-all jobs: unit-tests: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI run: | diff --git a/.github/workflows/vtop_example.yml b/.github/workflows/vtop_example.yml new file mode 100644 index 00000000000..fb5ae87c101 --- /dev/null +++ b/.github/workflows/vtop_example.yml @@ -0,0 +1,97 @@ +name: vtop_example +on: [push, pull_request] +concurrency: + group: format('{0}-{1}', ${{ github.ref }}, 'vtop_example') + cancel-in-progress: true + +jobs: + build: + name: VTop Example + runs-on: self-hosted + + 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' + - 'test.go' + - 'Makefile' + - 'build.env' + - 'go.[sumod]' + - 'proto/*.proto' + - 'tools/**' + - 'config/**' + - 'bootstrap.sh' + - 'examples/**' + - 'test/**' + - '.github/workflows/vtop_example.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-file: go.mod + + - name: Tune the OS + if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' + run: | + echo '1024 65535' | sudo tee -a /proc/sys/net/ipv4/ip_local_port_range + + - name: Get dependencies + if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' + run: | + # Install everything we need, and configure + sudo apt-get install -y eatmydata make + go mod download + + # needed for vtctldclient + - name: Build vitess + if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' + run: | + make build + + - name: Install kubectl & kind + if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' + run: | + make install_kubectl_kind + + - name: vtop_example + if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' + timeout-minutes: 60 + run: | + source build.env + eatmydata -- go run test.go -docker=false -skip-build -print-log -follow -retry=1 -timeout=60m vtop_example \ No newline at end of file diff --git a/changelog/19.0/19.0.8/changelog.md b/changelog/19.0/19.0.8/changelog.md new file mode 100644 index 00000000000..97995e779b7 --- /dev/null +++ b/changelog/19.0/19.0.8/changelog.md @@ -0,0 +1,29 @@ +# Changelog of Vitess v19.0.8 + +### Bug fixes +#### Topology + * [release-19.0] Close zookeeper topo connection on disconnect (#17136) [#17191](https://github.com/vitessio/vitess/pull/17191) +#### VTTablet + * [release-19.0] Fix deadlock in messager and health streamer (#17230) [#17233](https://github.com/vitessio/vitess/pull/17233) + * [release-19.0] Fix potential deadlock in health streamer (#17261) [#17268](https://github.com/vitessio/vitess/pull/17268) +### CI/Build +#### Build/CI + * [release-19.0] Specify Ubuntu 24.04 for all jobs (#17278) [#17280](https://github.com/vitessio/vitess/pull/17280) +#### Cluster management + * [release-19.0] Fix flakiness in `TestListenerShutdown` (#17024) [#17187](https://github.com/vitessio/vitess/pull/17187) +#### General + * [release-19.0] Upgrade the Golang version to `go1.22.9` [#17214](https://github.com/vitessio/vitess/pull/17214) +### Enhancement +#### Query Serving + * [release-19.0] Fix to prevent stopping buffering prematurely (#17013) [#17203](https://github.com/vitessio/vitess/pull/17203) +### Internal Cleanup +#### Build/CI + * [release-19.0] Change the name of the vitess-tester repository (#16917) [#17028](https://github.com/vitessio/vitess/pull/17028) +### Release +#### General + * [release-19.0] Bump to `v19.0.8-SNAPSHOT` after the `v19.0.7` release [#17158](https://github.com/vitessio/vitess/pull/17158) + * [release-19.0] Code Freeze for `v19.0.8` [#17310](https://github.com/vitessio/vitess/pull/17310) +### Testing +#### Build/CI + * [release-19.0] Flakes: Address flakiness in TestZkConnClosedOnDisconnect (#17194) [#17195](https://github.com/vitessio/vitess/pull/17195) + diff --git a/changelog/19.0/19.0.8/release_notes.md b/changelog/19.0/19.0.8/release_notes.md new file mode 100644 index 00000000000..ffb601fba4f --- /dev/null +++ b/changelog/19.0/19.0.8/release_notes.md @@ -0,0 +1,7 @@ +# Release of Vitess v19.0.8 +The entire changelog for this release can be found [here](https://github.com/vitessio/vitess/blob/main/changelog/19.0/19.0.8/changelog.md). + +The release includes 11 merged Pull Requests. + +Thanks to all our contributors: @app/vitess-bot, @frouioui, @vitess-bot + diff --git a/changelog/19.0/README.md b/changelog/19.0/README.md index 008c92c2aec..5893d3b1f4c 100644 --- a/changelog/19.0/README.md +++ b/changelog/19.0/README.md @@ -1,4 +1,8 @@ ## v19.0 +* **[19.0.8](19.0.8)** + * [Changelog](19.0.8/changelog.md) + * [Release Notes](19.0.8/release_notes.md) + * **[19.0.7](19.0.7)** * [Changelog](19.0.7/changelog.md) * [Release Notes](19.0.7/release_notes.md) diff --git a/changelog/20.0/20.0.4/changelog.md b/changelog/20.0/20.0.4/changelog.md new file mode 100644 index 00000000000..ec4af560368 --- /dev/null +++ b/changelog/20.0/20.0.4/changelog.md @@ -0,0 +1,27 @@ +# Changelog of Vitess v20.0.4 + +### Bug fixes +#### Query Serving + * [release-20.0] Use proper keyspace when updating the query graph of a reference DML (#17226) [#17257](https://github.com/vitessio/vitess/pull/17257) +#### Topology + * [release-20.0] Close zookeeper topo connection on disconnect (#17136) [#17192](https://github.com/vitessio/vitess/pull/17192) +#### VTTablet + * [release-20.0] Fix deadlock in messager and health streamer (#17230) [#17234](https://github.com/vitessio/vitess/pull/17234) + * [release-20.0] Fix potential deadlock in health streamer (#17261) [#17269](https://github.com/vitessio/vitess/pull/17269) +### CI/Build +#### Build/CI + * [release-20.0] Specify Ubuntu 24.04 for all jobs (#17278) [#17281](https://github.com/vitessio/vitess/pull/17281) +#### Cluster management + * [release-20.0] Fix flakiness in `TestListenerShutdown` (#17024) [#17188](https://github.com/vitessio/vitess/pull/17188) +#### General + * [release-20.0] Upgrade the Golang version to `go1.22.9` [#17212](https://github.com/vitessio/vitess/pull/17212) +### Enhancement +#### Query Serving + * [release-20.0] Fix to prevent stopping buffering prematurely (#17013) [#17204](https://github.com/vitessio/vitess/pull/17204) +### Internal Cleanup +#### Build/CI + * [release-20.0] Change the name of the vitess-tester repository (#16917) [#17029](https://github.com/vitessio/vitess/pull/17029) +### Testing +#### Build/CI + * [release-20.0] Flakes: Address flakiness in TestZkConnClosedOnDisconnect (#17194) [#17196](https://github.com/vitessio/vitess/pull/17196) + diff --git a/changelog/20.0/20.0.4/release_notes.md b/changelog/20.0/20.0.4/release_notes.md new file mode 100644 index 00000000000..42dc5b2b8a3 --- /dev/null +++ b/changelog/20.0/20.0.4/release_notes.md @@ -0,0 +1,7 @@ +# Release of Vitess v20.0.4 +The entire changelog for this release can be found [here](https://github.com/vitessio/vitess/blob/main/changelog/20.0/20.0.4/changelog.md). + +The release includes 10 merged Pull Requests. + +Thanks to all our contributors: @app/vitess-bot, @frouioui + diff --git a/changelog/20.0/README.md b/changelog/20.0/README.md index f41ea711fb8..2fe6e3d9d61 100644 --- a/changelog/20.0/README.md +++ b/changelog/20.0/README.md @@ -1,4 +1,8 @@ ## v20.0 +* **[20.0.4](20.0.4)** + * [Changelog](20.0.4/changelog.md) + * [Release Notes](20.0.4/release_notes.md) + * **[20.0.3](20.0.3)** * [Changelog](20.0.3/changelog.md) * [Release Notes](20.0.3/release_notes.md) diff --git a/changelog/21.0/21.0.0/summary.md b/changelog/21.0/21.0.0/summary.md index 512aa45a12f..1c34f5ad81a 100644 --- a/changelog/21.0/21.0.0/summary.md +++ b/changelog/21.0/21.0.0/summary.md @@ -9,6 +9,7 @@ - [Deprecated VTTablet Flags](#vttablet-flags) - [Deletion of deprecated metrics](#metric-deletion) - [Deprecated Metrics](#deprecations-metrics) + - **[RPC Changes](#rpc-changes)** - **[Traffic Mirroring](#traffic-mirroring)** - **[Atomic Distributed Transaction Support](#atomic-transaction)** - **[New VTGate Shutdown Behavior](#new-vtgate-shutdown-behavior)** @@ -77,6 +78,12 @@ The following metrics are now deprecated and will be deleted in a future release | `vttablet` | `QueryCacheHits` | `QueryEnginePlanCacheHits` | | `vttablet` | `QueryCacheMisses` | `QueryEnginePlanCacheMisses` | +### RPC Changes + +These are the RPC changes made in this release - +1. `ReadReparentJournalInfo` RPC has been added in TabletManagerClient interface, that is going to be used in EmergencyReparentShard for better errant GTID detection. +2. `PrimaryStatus` RPC in TabletManagerClient interface has been updated to also return the server UUID of the primary. This is going to be used in the vttablets so that they can do their own errant GTID detection in `SetReplicationSource`. + ### Traffic Mirroring Traffic mirroring is intended to help reduce some of the uncertainty inherent to `MoveTables SwitchTraffic`. When diff --git a/changelog/21.0/21.0.1/changelog.md b/changelog/21.0/21.0.1/changelog.md new file mode 100644 index 00000000000..1410591c3d5 --- /dev/null +++ b/changelog/21.0/21.0.1/changelog.md @@ -0,0 +1,57 @@ +# Changelog of Vitess v21.0.1 + +### Bug fixes +#### Backup and Restore + * [release-21.0] Fix how we cancel the context in the builtin backup engine (#17285) [#17291](https://github.com/vitessio/vitess/pull/17291) + * [release-21.0] S3: optional endpoint resolver and correct retrier [#17307](https://github.com/vitessio/vitess/pull/17307) +#### Cluster management + * [release-21.0] Fix panic in vttablet when closing topo server twice (#17094) [#17122](https://github.com/vitessio/vitess/pull/17122) +#### Online DDL + * [release-21.0] Online DDL: fix defer function, potential connection pool exhaustion (#17207) [#17210](https://github.com/vitessio/vitess/pull/17210) +#### Query Serving + * [release-21.0] bugfix: treat EXPLAIN like SELECT (#17054) [#17058](https://github.com/vitessio/vitess/pull/17058) + * [release-21.0] Delegate Column Availability Checks to MySQL for Single-Route Queries (#17077) [#17087](https://github.com/vitessio/vitess/pull/17087) + * [release-21.0] bugfix: Handle CTEs with columns named in the CTE def (#17179) [#17181](https://github.com/vitessio/vitess/pull/17181) + * [release-21.0] Use proper keyspace when updating the query graph of a reference DML (#17226) [#17258](https://github.com/vitessio/vitess/pull/17258) +#### Topology + * [release-21.0] Close zookeeper topo connection on disconnect (#17136) [#17193](https://github.com/vitessio/vitess/pull/17193) +#### VReplication + * [release-21.0] VReplication: Qualify and SQL escape tables in created AutoIncrement VSchema definitions (#17174) [#17176](https://github.com/vitessio/vitess/pull/17176) +#### VTTablet + * [release-21.0] Fix deadlock in messager and health streamer (#17230) [#17235](https://github.com/vitessio/vitess/pull/17235) + * [release-21.0] Fix potential deadlock in health streamer (#17261) [#17270](https://github.com/vitessio/vitess/pull/17270) +### CI/Build +#### Build/CI + * [release-21.0] Specify Ubuntu 24.04 for all jobs (#17278) [#17282](https://github.com/vitessio/vitess/pull/17282) +#### Cluster management + * [release-21.0] Fix flakiness in `TestListenerShutdown` (#17024) [#17189](https://github.com/vitessio/vitess/pull/17189) +#### General + * [release-21.0] Upgrade the Golang version to `go1.23.3` [#17211](https://github.com/vitessio/vitess/pull/17211) +### Dependencies +#### Java + * [release-21.0] java package updates for grpc and protobuf and release plugins (#17100) [#17105](https://github.com/vitessio/vitess/pull/17105) +### Documentation +#### Documentation + * [Direct PR][release-21.0] Add RPC changes segment in the summary doc [#17034](https://github.com/vitessio/vitess/pull/17034) +### Enhancement +#### Online DDL + * [release-21.0] Improve Schema Engine's TablesWithSize80 query (#17066) [#17091](https://github.com/vitessio/vitess/pull/17091) +#### Query Serving + * [release-21.0] Fix to prevent stopping buffering prematurely (#17013) [#17205](https://github.com/vitessio/vitess/pull/17205) +#### VReplication + * [release-21.0] Binlog: Improve ZstdInMemoryDecompressorMaxSize management (#17220) [#17241](https://github.com/vitessio/vitess/pull/17241) +### Internal Cleanup +#### Build/CI + * [release-21.0] Change the name of the vitess-tester repository (#16917) [#17030](https://github.com/vitessio/vitess/pull/17030) +### Regression +#### Backup and Restore + * [release-21.0] Fix unreachable errors when taking a backup (#17062) [#17112](https://github.com/vitessio/vitess/pull/17112) +### Release +#### General + * [release-21.0] Bump to `v21.0.1-SNAPSHOT` after the `v21.0.0` release [#17098](https://github.com/vitessio/vitess/pull/17098) +### Testing +#### Build/CI + * [release-21.0] Flakes: Address flakiness in TestZkConnClosedOnDisconnect (#17194) [#17197](https://github.com/vitessio/vitess/pull/17197) +#### Query Serving + * [release-21.0] fix: flaky test on twopc transaction (#17068) [#17070](https://github.com/vitessio/vitess/pull/17070) + diff --git a/changelog/21.0/21.0.1/release_notes.md b/changelog/21.0/21.0.1/release_notes.md new file mode 100644 index 00000000000..e42d16379a6 --- /dev/null +++ b/changelog/21.0/21.0.1/release_notes.md @@ -0,0 +1,7 @@ +# Release of Vitess v21.0.1 +The entire changelog for this release can be found [here](https://github.com/vitessio/vitess/blob/main/changelog/21.0/21.0.1/changelog.md). + +The release includes 25 merged Pull Requests. + +Thanks to all our contributors: @GuptaManan100, @app/vitess-bot, @frouioui, @vitess-bot + diff --git a/changelog/21.0/README.md b/changelog/21.0/README.md index a77e98bcaba..f3a98feb55a 100644 --- a/changelog/21.0/README.md +++ b/changelog/21.0/README.md @@ -1,4 +1,8 @@ ## v21.0 +* **[21.0.1](21.0.1)** + * [Changelog](21.0.1/changelog.md) + * [Release Notes](21.0.1/release_notes.md) + * **[21.0.0](21.0.0)** * [Changelog](21.0.0/changelog.md) * [Release Notes](21.0.0/release_notes.md) diff --git a/changelog/22.0/22.0.0/summary.md b/changelog/22.0/22.0.0/summary.md index 7c7257bfae3..d21acf48a30 100644 --- a/changelog/22.0/22.0.0/summary.md +++ b/changelog/22.0/22.0.0/summary.md @@ -5,6 +5,7 @@ - **[Major Changes](#major-changes)** - **[RPC Changes](#rpc-changes)** - **[Prefer not promoting a replica that is currently taking a backup](#reparents-prefer-not-backing-up)** + - **[VTOrc Config File Changes](#vtorc-config-file-changes)** ## Major Changes @@ -25,4 +26,25 @@ For planned reparents, hosts taking backups with a backup engine other than `bui valid candidates. This means they will never get promoted - not even if there's no other candidates. Note that behavior for `builtin` backups remains unchanged: a replica that is currently taking a `builtin` backup will -never be promoted, neither by planned nor by emergency reparents. \ No newline at end of file +never be promoted, neither by planned nor by emergency reparents. + +### VTOrc Config File Changes + +The configuration file for VTOrc has been updated to now support dynamic fields. The old `--config` parameter has been removed. The alternative is to use the `--config-file` parameter. The configuration can now be provided in json, yaml or any other format that [viper](https://github.com/spf13/viper) supports. + +The following fields can be dynamically changed - +1. `instance-poll-time` +2. `prevent-cross-cell-failover` +3. `snapshot-topology-interval` +4. `reasonable-replication-lag` +5. `audit-to-backend` +6. `audit-to-syslog` +7. `audit-purge-duration` +8. `wait-replicas-timeout` +9. `tolerable-replication-lag` +10. `topo-information-refresh-duration` +11. `recovery-poll-duration` +12. `allow-emergency-reparent` +13. `change-tablets-with-errant-gtid-to-drained` + +To upgrade to the newer version of the configuration file, first switch to using the flags in your current deployment before upgrading. Then you can switch to using the configuration file in the newer release. diff --git a/examples/common/scripts/vtctld-up.sh b/examples/common/scripts/vtctld-up.sh index 6902a851997..4a4b2587c4f 100755 --- a/examples/common/scripts/vtctld-up.sh +++ b/examples/common/scripts/vtctld-up.sh @@ -33,6 +33,7 @@ vtctld \ --port $vtctld_web_port \ --grpc_port $grpc_port \ --pid_file $VTDATAROOT/tmp/vtctld.pid \ + --pprof-http \ > $VTDATAROOT/tmp/vtctld.out 2>&1 & for _ in {0..300}; do diff --git a/examples/common/scripts/vtgate-up.sh b/examples/common/scripts/vtgate-up.sh index dbaaad02367..fd7860cf6ba 100755 --- a/examples/common/scripts/vtgate-up.sh +++ b/examples/common/scripts/vtgate-up.sh @@ -41,6 +41,7 @@ vtgate \ --pid_file $VTDATAROOT/tmp/vtgate.pid \ --enable_buffer \ --mysql_auth_server_impl none \ + --pprof-http \ > $VTDATAROOT/tmp/vtgate.out 2>&1 & # Block waiting for vtgate to be listening diff --git a/examples/common/scripts/vtorc-up.sh b/examples/common/scripts/vtorc-up.sh index 23ca4e62b48..807f522b1f7 100755 --- a/examples/common/scripts/vtorc-up.sh +++ b/examples/common/scripts/vtorc-up.sh @@ -11,7 +11,9 @@ vtorc \ $TOPOLOGY_FLAGS \ --logtostderr \ --alsologtostderr \ - --config="${script_dir}/../vtorc/config.json" \ + --config-path="${script_dir}/../vtorc/" \ + --config-name="config.yaml" \ + --config-type="yml" \ --port $port \ > "${log_dir}/vtorc.out" 2>&1 & diff --git a/examples/common/scripts/vttablet-up.sh b/examples/common/scripts/vttablet-up.sh index daa40aee894..282cd0553ea 100755 --- a/examples/common/scripts/vttablet-up.sh +++ b/examples/common/scripts/vttablet-up.sh @@ -54,6 +54,7 @@ vttablet \ --service_map 'grpc-queryservice,grpc-tabletmanager,grpc-updatestream' \ --pid_file $VTDATAROOT/$tablet_dir/vttablet.pid \ --heartbeat_on_demand_duration=5s \ + --pprof-http \ > $VTDATAROOT/$tablet_dir/vttablet.out 2>&1 & # Block waiting for the tablet to be listening diff --git a/examples/common/vtorc/config.json b/examples/common/vtorc/config.json deleted file mode 100644 index 53b012c2162..00000000000 --- a/examples/common/vtorc/config.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "RecoveryPeriodBlockSeconds": 1, - "InstancePollSeconds": 1 -} \ No newline at end of file diff --git a/examples/common/vtorc/config.yaml b/examples/common/vtorc/config.yaml new file mode 100644 index 00000000000..26af59afac8 --- /dev/null +++ b/examples/common/vtorc/config.yaml @@ -0,0 +1,13 @@ +instance-poll-time: 1s +prevent-cross-cell-failover: false +snapshot-topology-interval: 0h +reasonable-replication-lag: 10s +audit-to-backend: false +audit-to-syslog: false +audit-purge-duration: 168h +wait-replicas-timeout: 30s +tolerable-replication-lag: 0s +topo-information-refresh-duration: 15s +recovery-poll-duration: 1s +allow-emergency-reparent: true +change-tablets-with-errant-gtid-to-drained: false diff --git a/examples/operator/101_initial_cluster.yaml b/examples/operator/101_initial_cluster.yaml index c26219254f1..c044141dd4b 100644 --- a/examples/operator/101_initial_cluster.yaml +++ b/examples/operator/101_initial_cluster.yaml @@ -15,7 +15,7 @@ spec: vtbackup: vitess/lite:latest vtorc: vitess/lite:latest mysqld: - mysql80Compatible: vitess/lite:latest + mysql80Compatible: mysql:8.0.30 mysqldExporter: prom/mysqld-exporter:v0.11.0 cells: - name: zone1 @@ -79,7 +79,7 @@ spec: cpu: 100m memory: 128Mi extraFlags: - recovery-period-block-duration: 5s + instance-poll-time: 1s partitionings: - equal: parts: 1 @@ -155,23 +155,6 @@ stringData: # Vitess defaults ############################################################################### - # Vitess-internal database. - CREATE DATABASE IF NOT EXISTS _vt; - # Note that definitions of local_metadata and shard_metadata should be the same - # as in production which is defined in go/vt/mysqlctl/metadata_tables.go. - CREATE TABLE IF NOT EXISTS _vt.local_metadata ( - name VARCHAR(255) NOT NULL, - value VARCHAR(255) NOT NULL, - db_name VARBINARY(255) NOT NULL, - PRIMARY KEY (db_name, name) - ) ENGINE=InnoDB; - CREATE TABLE IF NOT EXISTS _vt.shard_metadata ( - name VARCHAR(255) NOT NULL, - value MEDIUMBLOB NOT NULL, - db_name VARBINARY(255) NOT NULL, - PRIMARY KEY (db_name, name) - ) ENGINE=InnoDB; - # Admin user with all privileges. CREATE USER 'vt_dba'@'localhost'; GRANT ALL ON *.* TO 'vt_dba'@'localhost'; @@ -200,12 +183,10 @@ stringData: ON *.* TO 'vt_allprivs'@'localhost'; # User for slave replication connections. - # TODO: Should we set a password on this since it allows remote connections? CREATE USER 'vt_repl'@'%'; GRANT REPLICATION SLAVE ON *.* TO 'vt_repl'@'%'; - # User for Vitess filtered replication (binlog player). - # Same permissions as vt_app. + # User for Vitess VReplication (base vstreamers and vplayer). CREATE USER 'vt_filtered'@'localhost'; GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, RELOAD, PROCESS, FILE, REFERENCES, INDEX, ALTER, SHOW DATABASES, CREATE TEMPORARY TABLES, @@ -213,6 +194,13 @@ stringData: SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, CREATE USER, EVENT, TRIGGER ON *.* TO 'vt_filtered'@'localhost'; + # User for general MySQL monitoring. + CREATE USER 'vt_monitoring'@'localhost'; + GRANT SELECT, PROCESS, SUPER, REPLICATION CLIENT, RELOAD + ON *.* TO 'vt_monitoring'@'localhost'; + GRANT SELECT, UPDATE, DELETE, DROP + ON performance_schema.* TO 'vt_monitoring'@'localhost'; + # custom sql is used to add custom scripts like creating users/passwords. We use it in our tests # {{custom_sql}} diff --git a/examples/operator/201_customer_tablets.yaml b/examples/operator/201_customer_tablets.yaml index 5800a5e05df..a16723ade57 100644 --- a/examples/operator/201_customer_tablets.yaml +++ b/examples/operator/201_customer_tablets.yaml @@ -75,7 +75,7 @@ spec: cpu: 100m memory: 128Mi extraFlags: - recovery-period-block-duration: 5s + instance-poll-time: 1s partitionings: - equal: parts: 1 diff --git a/examples/operator/302_new_shards.yaml b/examples/operator/302_new_shards.yaml index 2e15bc40d28..b954dab7beb 100644 --- a/examples/operator/302_new_shards.yaml +++ b/examples/operator/302_new_shards.yaml @@ -75,7 +75,7 @@ spec: cpu: 100m memory: 128Mi extraFlags: - recovery-period-block-duration: 5s + instance-poll-time: 1s partitionings: - equal: parts: 1 diff --git a/examples/operator/306_down_shard_0.yaml b/examples/operator/306_down_shard_0.yaml index 4bdb694d678..7206d1b4e64 100644 --- a/examples/operator/306_down_shard_0.yaml +++ b/examples/operator/306_down_shard_0.yaml @@ -75,7 +75,7 @@ spec: cpu: 100m memory: 128Mi extraFlags: - recovery-period-block-duration: 5s + instance-poll-time: 1s partitionings: - equal: parts: 1 diff --git a/examples/operator/401_scheduled_backups.yaml b/examples/operator/401_scheduled_backups.yaml index 0e74ada8478..9e8ed29aa9f 100644 --- a/examples/operator/401_scheduled_backups.yaml +++ b/examples/operator/401_scheduled_backups.yaml @@ -116,7 +116,7 @@ spec: cpu: 100m memory: 128Mi extraFlags: - recovery-period-block-duration: 5s + instance-poll-time: 1s partitionings: - equal: parts: 1 diff --git a/go/cmd/vtorc/cli/cli.go b/go/cmd/vtorc/cli/cli.go index 1233c1e2ac2..b79793c6492 100644 --- a/go/cmd/vtorc/cli/cli.go +++ b/go/cmd/vtorc/cli/cli.go @@ -20,6 +20,7 @@ import ( "github.com/spf13/cobra" "vitess.io/vitess/go/acl" + "vitess.io/vitess/go/viperutil/debug" "vitess.io/vitess/go/vt/log" "vitess.io/vitess/go/vt/servenv" "vitess.io/vitess/go/vt/vtorc/config" @@ -29,8 +30,7 @@ import ( ) var ( - configFile string - Main = &cobra.Command{ + Main = &cobra.Command{ Use: "vtorc", Short: "VTOrc is the automated fault detection and repair tool in Vitess.", Example: `vtorc \ @@ -51,22 +51,16 @@ var ( func run(cmd *cobra.Command, args []string) { servenv.Init() - config.UpdateConfigValuesFromFlags() inst.RegisterStats() log.Info("starting vtorc") - if len(configFile) > 0 { - config.ForceRead(configFile) - } else { - config.Read("/etc/vtorc.conf.json", "conf/vtorc.conf.json", "vtorc.conf.json") - } - if config.Config.AuditToSyslog { + if config.GetAuditToSyslog() { inst.EnableAuditSyslog() } config.MarkConfigurationLoaded() // Log final config values to debug if something goes wrong. - config.LogConfigValues() + log.Infof("Running with Configuration - %v", debug.AllSettings()) server.StartVTOrcDiscovery() server.RegisterVTOrcAPIEndpoints() @@ -96,7 +90,5 @@ func init() { servenv.MoveFlagsToCobraCommand(Main) logic.RegisterFlags(Main.Flags()) - config.RegisterFlags(Main.Flags()) acl.RegisterFlags(Main.Flags()) - Main.Flags().StringVar(&configFile, "config", "", "config file name") } diff --git a/go/flags/endtoend/vtcombo.txt b/go/flags/endtoend/vtcombo.txt index 01a391d0cad..c10d045ecbd 100644 --- a/go/flags/endtoend/vtcombo.txt +++ b/go/flags/endtoend/vtcombo.txt @@ -414,7 +414,7 @@ Flags: --vreplication_copy_phase_duration duration Duration for each copy phase loop (before running the next catchup: default 1h) (default 1h0m0s) --vreplication_copy_phase_max_innodb_history_list_length int The maximum InnoDB transaction history that can exist on a vstreamer (source) before starting another round of copying rows. This helps to limit the impact on the source tablet. (default 1000000) --vreplication_copy_phase_max_mysql_replication_lag int The maximum MySQL replication lag (in seconds) that can exist on a vstreamer (source) before starting another round of copying rows. This helps to limit the impact on the source tablet. (default 43200) - --vreplication_experimental_flags int (Bitmask) of experimental features in vreplication to enable (default 3) + --vreplication_experimental_flags int (Bitmask) of experimental features in vreplication to enable (default 7) --vreplication_heartbeat_update_interval int Frequency (in seconds, default 1, max 60) at which the time_updated column of a vreplication stream when idling (default 1) --vreplication_max_time_to_retry_on_error duration stop automatically retrying when we've had consecutive failures with the same error for this long after the first occurrence --vreplication_net_read_timeout int Session value of net_read_timeout for vreplication, in seconds (default 300) diff --git a/go/flags/endtoend/vtorc.txt b/go/flags/endtoend/vtorc.txt index d34b4404df7..efccb0afdfc 100644 --- a/go/flags/endtoend/vtorc.txt +++ b/go/flags/endtoend/vtorc.txt @@ -25,7 +25,6 @@ Flags: --catch-sigpipe catch and ignore SIGPIPE on stdout and stderr if specified --change-tablets-with-errant-gtid-to-drained Whether VTOrc should be changing the type of tablets with errant GTIDs to DRAINED --clusters_to_watch strings Comma-separated list of keyspaces or keyspace/shards that this instance will monitor and repair. Defaults to all clusters in the topology. Example: "ks1,ks2/-80" - --config string config file name --config-file string Full path of the config file (with extension) to use. If set, --config-path, --config-type, and --config-name are ignored. --config-file-not-found-handling ConfigFileNotFoundHandling Behavior when a config file is not found. (Options: error, exit, ignore, warn) (default warn) --config-name string Name of the config file (without extension) to search for. (default "vtconfig") diff --git a/go/flags/endtoend/vttablet.txt b/go/flags/endtoend/vttablet.txt index 8be7b620469..f79db05f327 100644 --- a/go/flags/endtoend/vttablet.txt +++ b/go/flags/endtoend/vttablet.txt @@ -414,7 +414,7 @@ Flags: --vreplication_copy_phase_duration duration Duration for each copy phase loop (before running the next catchup: default 1h) (default 1h0m0s) --vreplication_copy_phase_max_innodb_history_list_length int The maximum InnoDB transaction history that can exist on a vstreamer (source) before starting another round of copying rows. This helps to limit the impact on the source tablet. (default 1000000) --vreplication_copy_phase_max_mysql_replication_lag int The maximum MySQL replication lag (in seconds) that can exist on a vstreamer (source) before starting another round of copying rows. This helps to limit the impact on the source tablet. (default 43200) - --vreplication_experimental_flags int (Bitmask) of experimental features in vreplication to enable (default 3) + --vreplication_experimental_flags int (Bitmask) of experimental features in vreplication to enable (default 7) --vreplication_heartbeat_update_interval int Frequency (in seconds, default 1, max 60) at which the time_updated column of a vreplication stream when idling (default 1) --vreplication_max_time_to_retry_on_error duration stop automatically retrying when we've had consecutive failures with the same error for this long after the first occurrence --vreplication_net_read_timeout int Session value of net_read_timeout for vreplication, in seconds (default 300) diff --git a/go/test/endtoend/cluster/cluster_process.go b/go/test/endtoend/cluster/cluster_process.go index 6f800a70a49..b89e007b4f2 100644 --- a/go/test/endtoend/cluster/cluster_process.go +++ b/go/test/endtoend/cluster/cluster_process.go @@ -1301,7 +1301,6 @@ func (cluster *LocalProcessCluster) NewVTOrcProcess(config VTOrcConfiguration) * VtctlProcess: *base, LogDir: cluster.TmpDirectory, Config: config, - WebPort: cluster.GetAndReservePort(), Port: cluster.GetAndReservePort(), } } diff --git a/go/test/endtoend/cluster/vtctldclient_process.go b/go/test/endtoend/cluster/vtctldclient_process.go index 892f03e27fe..8b401f59070 100644 --- a/go/test/endtoend/cluster/vtctldclient_process.go +++ b/go/test/endtoend/cluster/vtctldclient_process.go @@ -41,6 +41,7 @@ type VtctldClientProcess struct { TempDirectory string ZoneName string VtctldClientMajorVersion int + Quiet bool } // ExecuteCommand executes any vtctldclient command @@ -72,7 +73,9 @@ func (vtctldclient *VtctldClientProcess) ExecuteCommandWithOutput(args ...string filterDoubleDashArgs(pArgs, vtctldclient.VtctldClientMajorVersion)..., ) msg := binlogplayer.LimitString(strings.Join(tmpProcess.Args, " "), 256) // limit log line length - log.Infof("Executing vtctldclient with command: %v (attempt %d of %d)", msg, i+1, retries) + if !vtctldclient.Quiet { + log.Infof("Executing vtctldclient with command: %v (attempt %d of %d)", msg, i+1, retries) + } resultByte, err = tmpProcess.CombinedOutput() resultStr = string(resultByte) if err == nil || !shouldRetry(resultStr) { diff --git a/go/test/endtoend/cluster/vtorc_process.go b/go/test/endtoend/cluster/vtorc_process.go index 4fcb68e292d..af101a8bebd 100644 --- a/go/test/endtoend/cluster/vtorc_process.go +++ b/go/test/endtoend/cluster/vtorc_process.go @@ -43,20 +43,28 @@ type VTOrcProcess struct { ExtraArgs []string ConfigPath string Config VTOrcConfiguration - WebPort int + NoOverride bool proc *exec.Cmd exit chan error } type VTOrcConfiguration struct { - Debug bool - ListenAddress string - RecoveryPeriodBlockSeconds int - TopologyRefreshSeconds int `json:",omitempty"` - PreventCrossDataCenterPrimaryFailover bool `json:",omitempty"` - LockShardTimeoutSeconds int `json:",omitempty"` - ReplicationLagQuery string `json:",omitempty"` - FailPrimaryPromotionOnLagMinutes int `json:",omitempty"` + InstancePollTime string `json:"instance-poll-time,omitempty"` + SnapshotTopologyInterval string `json:"snapshot-topology-interval,omitempty"` + PreventCrossCellFailover bool `json:"prevent-cross-cell-failover,omitempty"` + ReasonableReplicationLag string `json:"reasonable-replication-lag,omitempty"` + AuditToBackend bool `json:"audit-to-backend,omitempty"` + AuditToSyslog bool `json:"audit-to-syslog,omitempty"` + AuditPurgeDuration string `json:"audit-purge-duration,omitempty"` + WaitReplicasTimeout string `json:"wait-replicas-timeout,omitempty"` + TolerableReplicationLag string `json:"tolerable-replication-lag,omitempty"` + TopoInformationRefreshDuration string `json:"topo-information-refresh-duration,omitempty"` + RecoveryPollDuration string `json:"recovery-poll-duration,omitempty"` + AllowEmergencyReparent string `json:"allow-emergency-reparent,omitempty"` + ChangeTabletsWithErrantGtidToDrained bool `json:"change-tablets-with-errant-gtid-to-drained,omitempty"` + LockShardTimeoutSeconds int `json:",omitempty"` + ReplicationLagQuery string `json:",omitempty"` + FailPrimaryPromotionOnLagMinutes int `json:",omitempty"` } // ToJSONString will marshal this configuration as JSON @@ -65,12 +73,12 @@ func (config *VTOrcConfiguration) ToJSONString() string { return string(b) } -func (config *VTOrcConfiguration) AddDefaults(webPort int) { - config.Debug = true - if config.RecoveryPeriodBlockSeconds == 0 { - config.RecoveryPeriodBlockSeconds = 1 - } - config.ListenAddress = fmt.Sprintf(":%d", webPort) +func (config *VTOrcConfiguration) addValuesToCheckOverride() { + config.InstancePollTime = "10h" +} + +func (orc *VTOrcProcess) RewriteConfiguration() error { + return os.WriteFile(orc.ConfigPath, []byte(orc.Config.ToJSONString()), 0644) } // Setup starts orc process with required arguements @@ -91,7 +99,9 @@ func (orc *VTOrcProcess) Setup() (err error) { orc.ConfigPath = configFile.Name() // Add the default configurations and print them out - orc.Config.AddDefaults(orc.WebPort) + if !orc.NoOverride { + orc.Config.addValuesToCheckOverride() + } log.Errorf("configuration - %v", orc.Config.ToJSONString()) _, err = configFile.WriteString(orc.Config.ToJSONString()) if err != nil { @@ -111,15 +121,18 @@ func (orc *VTOrcProcess) Setup() (err error) { "--topo_implementation", orc.TopoImplementation, "--topo_global_server_address", orc.TopoGlobalAddress, "--topo_global_root", orc.TopoGlobalRoot, - "--config", orc.ConfigPath, + "--config-file", orc.ConfigPath, "--port", fmt.Sprintf("%d", orc.Port), - // This parameter is overriden from the config file, added here to just verify that we indeed use the config file paramter over the flag - "--recovery-period-block-duration", "10h", - "--instance-poll-time", "1s", - // Faster topo information refresh speeds up the tests. This doesn't add any significant load either - "--topo-information-refresh-duration", "3s", "--bind-address", "127.0.0.1", ) + if !orc.NoOverride { + orc.proc.Args = append(orc.proc.Args, + // This parameter is overriden from the config file. This verifies that we indeed use the flag value over the config file. + "--instance-poll-time", "1s", + // Faster topo information refresh speeds up the tests. This doesn't add any significant load either. + "--topo-information-refresh-duration", "3s", + ) + } if *isCoverage { orc.proc.Args = append(orc.proc.Args, "--test.coverprofile="+getCoveragePath("orc.out")) diff --git a/go/test/endtoend/cluster/vttablet_process.go b/go/test/endtoend/cluster/vttablet_process.go index 6c7a85ec533..6bc6e6b8d7f 100644 --- a/go/test/endtoend/cluster/vttablet_process.go +++ b/go/test/endtoend/cluster/vttablet_process.go @@ -459,6 +459,16 @@ func (vttablet *VttabletProcess) QueryTablet(query string, keyspace string, useD return executeQuery(conn, query) } +// MultiQueryTablet lets you execute a query in this tablet and get the result +func (vttablet *VttabletProcess) MultiQueryTablet(sql string, keyspace string, useDb bool) error { + conn, err := vttablet.TabletConn(keyspace, useDb) + if err != nil { + return err + } + defer conn.Close() + return executeMultiQuery(conn, sql) +} + // SemiSyncExtensionLoaded returns what type of semi-sync extension is loaded func (vttablet *VttabletProcess) SemiSyncExtensionLoaded() (mysql.SemiSyncType, error) { conn, err := vttablet.TabletConn("", false) diff --git a/go/test/endtoend/onlineddl/flow/onlineddl_flow_test.go b/go/test/endtoend/onlineddl/flow/onlineddl_flow_test.go index c442c042f8a..035789e4b87 100644 --- a/go/test/endtoend/onlineddl/flow/onlineddl_flow_test.go +++ b/go/test/endtoend/onlineddl/flow/onlineddl_flow_test.go @@ -63,7 +63,6 @@ import ( "vitess.io/vitess/go/test/endtoend/throttler" "vitess.io/vitess/go/vt/log" "vitess.io/vitess/go/vt/schema" - vttablet "vitess.io/vitess/go/vt/vttablet/common" throttlebase "vitess.io/vitess/go/vt/vttablet/tabletserver/throttle/base" "vitess.io/vitess/go/vt/vttablet/tabletserver/throttle/throttlerapp" ) @@ -145,9 +144,6 @@ func TestMain(m *testing.M) { "--heartbeat_on_demand_duration", "5s", "--migration_check_interval", "2s", "--watch_replication_stream", - // Test VPlayer batching mode. - fmt.Sprintf("--vreplication_experimental_flags=%d", - vttablet.VReplicationExperimentalFlagAllowNoBlobBinlogRowImage|vttablet.VReplicationExperimentalFlagOptimizeInserts|vttablet.VReplicationExperimentalFlagVPlayerBatching), } clusterInstance.VtGateExtraArgs = []string{ "--ddl_strategy", "online", diff --git a/go/test/endtoend/onlineddl/vrepl_stress/onlineddl_vrepl_mini_stress_test.go b/go/test/endtoend/onlineddl/vrepl_stress/onlineddl_vrepl_mini_stress_test.go index e0dd9701cf8..88c145dc40c 100644 --- a/go/test/endtoend/onlineddl/vrepl_stress/onlineddl_vrepl_mini_stress_test.go +++ b/go/test/endtoend/onlineddl/vrepl_stress/onlineddl_vrepl_mini_stress_test.go @@ -38,7 +38,6 @@ import ( "vitess.io/vitess/go/test/endtoend/throttler" "vitess.io/vitess/go/vt/log" "vitess.io/vitess/go/vt/schema" - vttablet "vitess.io/vitess/go/vt/vttablet/common" ) type WriteMetrics struct { @@ -184,9 +183,6 @@ func TestMain(m *testing.M) { "--heartbeat_on_demand_duration", "5s", "--migration_check_interval", "5s", "--watch_replication_stream", - // Test VPlayer batching mode. - fmt.Sprintf("--vreplication_experimental_flags=%d", - vttablet.VReplicationExperimentalFlagAllowNoBlobBinlogRowImage|vttablet.VReplicationExperimentalFlagOptimizeInserts|vttablet.VReplicationExperimentalFlagVPlayerBatching), } clusterInstance.VtGateExtraArgs = []string{ "--ddl_strategy", "online", diff --git a/go/test/endtoend/onlineddl/vrepl_stress_suite/onlineddl_vrepl_stress_suite_test.go b/go/test/endtoend/onlineddl/vrepl_stress_suite/onlineddl_vrepl_stress_suite_test.go index 440b921f9ba..85b3585beb4 100644 --- a/go/test/endtoend/onlineddl/vrepl_stress_suite/onlineddl_vrepl_stress_suite_test.go +++ b/go/test/endtoend/onlineddl/vrepl_stress_suite/onlineddl_vrepl_stress_suite_test.go @@ -51,7 +51,6 @@ import ( "vitess.io/vitess/go/timer" "vitess.io/vitess/go/vt/log" "vitess.io/vitess/go/vt/schema" - vttablet "vitess.io/vitess/go/vt/vttablet/common" ) type testcase struct { @@ -436,9 +435,6 @@ func TestMain(m *testing.M) { "--migration_check_interval", "5s", "--vstream_packet_size", "4096", // Keep this value small and below 10k to ensure multilple vstream iterations "--watch_replication_stream", - // Test VPlayer batching mode. - fmt.Sprintf("--vreplication_experimental_flags=%d", - vttablet.VReplicationExperimentalFlagAllowNoBlobBinlogRowImage|vttablet.VReplicationExperimentalFlagOptimizeInserts|vttablet.VReplicationExperimentalFlagVPlayerBatching), } clusterInstance.VtGateExtraArgs = []string{ "--ddl_strategy", "online", diff --git a/go/test/endtoend/tabletmanager/throttler_topo/throttler_test.go b/go/test/endtoend/tabletmanager/throttler_topo/throttler_test.go index e8297f0fa8f..df727802648 100644 --- a/go/test/endtoend/tabletmanager/throttler_topo/throttler_test.go +++ b/go/test/endtoend/tabletmanager/throttler_topo/throttler_test.go @@ -175,6 +175,16 @@ func throttledApps(tablet *cluster.Vttablet) (resp *http.Response, respBody stri return resp, respBody, err } +func vitessThrottleCheck(tablet *cluster.Vttablet, skipRequestHeartbeats bool) (*vtctldatapb.CheckThrottlerResponse, error) { + flags := &throttle.CheckFlags{ + Scope: base.ShardScope, + SkipRequestHeartbeats: skipRequestHeartbeats, + MultiMetricsEnabled: true, + } + resp, err := throttler.CheckThrottler(clusterInstance, tablet, throttlerapp.VitessName, flags) + return resp, err +} + func throttleCheck(tablet *cluster.Vttablet, skipRequestHeartbeats bool) (*vtctldatapb.CheckThrottlerResponse, error) { flags := &throttle.CheckFlags{ Scope: base.ShardScope, @@ -305,6 +315,17 @@ func TestInitialThrottler(t *testing.T) { waitForThrottleCheckStatus(t, primaryTablet, tabletmanagerdatapb.CheckThrottlerResponseCode_THRESHOLD_EXCEEDED) }) t.Run("setting high threshold", func(t *testing.T) { + { + req := &vtctldatapb.UpdateThrottlerConfigRequest{MetricName: base.LoadAvgMetricName.String(), Threshold: 5555} + _, err := throttler.UpdateThrottlerTopoConfig(clusterInstance, req, nil, nil) + assert.NoError(t, err) + } + { + req := &vtctldatapb.UpdateThrottlerConfigRequest{MetricName: base.MysqldLoadAvgMetricName.String(), Threshold: 5555} + _, err := throttler.UpdateThrottlerTopoConfig(clusterInstance, req, nil, nil) + assert.NoError(t, err) + } + req := &vtctldatapb.UpdateThrottlerConfigRequest{Threshold: extremelyHighThreshold.Seconds()} _, err := throttler.UpdateThrottlerTopoConfig(clusterInstance, req, nil, nil) assert.NoError(t, err) @@ -317,6 +338,19 @@ func TestInitialThrottler(t *testing.T) { t.Run("validating OK response from throttler with high threshold", func(t *testing.T) { waitForThrottleCheckStatus(t, primaryTablet, tabletmanagerdatapb.CheckThrottlerResponseCode_OK) }) + t.Run("validating vitess app throttler check", func(t *testing.T) { + resp, err := vitessThrottleCheck(primaryTablet, true) + require.NoError(t, err) + for _, metricName := range base.KnownMetricNames { + t.Run(metricName.String(), func(t *testing.T) { + assert.Contains(t, resp.Check.Metrics, metricName.String()) + metric := resp.Check.Metrics[metricName.String()] + require.NotNil(t, metric) + assert.Equal(t, tabletmanagerdatapb.CheckThrottlerResponseCode_OK, metric.ResponseCode, "metric: %+v", metric) + }) + } + }) + t.Run("setting low threshold", func(t *testing.T) { req := &vtctldatapb.UpdateThrottlerConfigRequest{Threshold: throttler.DefaultThreshold.Seconds()} _, err := throttler.UpdateThrottlerTopoConfig(clusterInstance, req, nil, nil) diff --git a/go/test/endtoend/transaction/twopc/fuzz/fuzzer_test.go b/go/test/endtoend/transaction/twopc/fuzz/fuzzer_test.go index 75bc46bacab..da6486242df 100644 --- a/go/test/endtoend/transaction/twopc/fuzz/fuzzer_test.go +++ b/go/test/endtoend/transaction/twopc/fuzz/fuzzer_test.go @@ -53,7 +53,9 @@ var ( } insertIntoFuzzUpdate = "INSERT INTO twopc_fuzzer_update (id, col) VALUES (%d, %d)" + insertIntoFuzzMulti = "INSERT INTO twopc_fuzzer_multi (id) VALUES (%d)" updateFuzzUpdate = "UPDATE twopc_fuzzer_update SET col = col + %d WHERE id = %d" + updateFuzzUpdateMulti = "UPDATE twopc_fuzzer_update join twopc_fuzzer_multi using (id) SET col = col + %d WHERE id = %d" insertIntoFuzzInsert = "INSERT INTO twopc_fuzzer_insert (id, updateSet, threadId) VALUES (%d, %d, %d)" selectFromFuzzUpdate = "SELECT col FROM twopc_fuzzer_update WHERE id = %d" selectIdFromFuzzInsert = "SELECT threadId FROM twopc_fuzzer_insert WHERE updateSet = %d AND id = %d ORDER BY col" @@ -294,6 +296,10 @@ func (fz *fuzzer) initialize(t *testing.T, conn *mysql.Conn) { for _, id := range updateSet { _, err := conn.ExecuteFetch(fmt.Sprintf(insertIntoFuzzUpdate, id, 0), 0, false) require.NoError(t, err) + // We insert the same id values in multi table as we in the update table. We use this for running + // multi-table updates and inserts. + _, err = conn.ExecuteFetch(fmt.Sprintf(insertIntoFuzzMulti, id), 0, false) + require.NoError(t, err) } } } @@ -331,12 +337,20 @@ func (fz *fuzzer) generateAndExecuteTransaction(threadId int) { _, _ = conn.ExecuteFetch(finalCommand, 0, false) } +func getUpdateQuery(incrementVal int32, id int) string { + if rand.Intn(2) == 1 { + return fmt.Sprintf(updateFuzzUpdateMulti, incrementVal, id) + } + return fmt.Sprintf(updateFuzzUpdate, incrementVal, id) +} + // generateUpdateQueries generates the queries to run updates on the twopc_fuzzer_update table. // It takes the update set index and the value to increment the set by. func (fz *fuzzer) generateUpdateQueries(updateSet int, incrementVal int32) []string { var queries []string for _, id := range fz.updateRowsVals[updateSet] { - queries = append(queries, fmt.Sprintf(updateFuzzUpdate, incrementVal, id)) + // Use multi table DML queries half the time. + queries = append(queries, getUpdateQuery(incrementVal, id)) } rand.Shuffle(len(queries), func(i, j int) { queries[i], queries[j] = queries[j], queries[i] @@ -427,7 +441,7 @@ func (fz *fuzzer) randomDML() string { } // Generate UPDATE updateId := fz.updateRowsVals[rand.Intn(len(fz.updateRowsVals))][rand.Intn(len(updateRowBaseVals))] - return fmt.Sprintf(updateFuzzUpdate, rand.Intn(100000), updateId) + return getUpdateQuery(rand.Int31n(100000), updateId) } /* diff --git a/go/test/endtoend/transaction/twopc/fuzz/main_test.go b/go/test/endtoend/transaction/twopc/fuzz/main_test.go index 1b05615d51a..15574c8d072 100644 --- a/go/test/endtoend/transaction/twopc/fuzz/main_test.go +++ b/go/test/endtoend/transaction/twopc/fuzz/main_test.go @@ -126,5 +126,6 @@ func cleanup(t *testing.T) { utils.ClearOutTable(t, vtParams, "twopc_fuzzer_insert") utils.ClearOutTable(t, vtParams, "twopc_fuzzer_update") + utils.ClearOutTable(t, vtParams, "twopc_fuzzer_multi") utils.ClearOutTable(t, vtParams, "twopc_t1") } diff --git a/go/test/endtoend/transaction/twopc/fuzz/schema.sql b/go/test/endtoend/transaction/twopc/fuzz/schema.sql index 5173166bfd4..b070466087d 100644 --- a/go/test/endtoend/transaction/twopc/fuzz/schema.sql +++ b/go/test/endtoend/transaction/twopc/fuzz/schema.sql @@ -4,6 +4,11 @@ create table twopc_fuzzer_update ( primary key (id) ) Engine=InnoDB; +create table twopc_fuzzer_multi ( + id bigint, + primary key (id) +) Engine=InnoDB; + create table twopc_fuzzer_insert ( id bigint, updateSet bigint, diff --git a/go/test/endtoend/transaction/twopc/fuzz/vschema.json b/go/test/endtoend/transaction/twopc/fuzz/vschema.json index 415b5958f54..83107bc96ff 100644 --- a/go/test/endtoend/transaction/twopc/fuzz/vschema.json +++ b/go/test/endtoend/transaction/twopc/fuzz/vschema.json @@ -3,6 +3,9 @@ "vindexes": { "reverse_bits": { "type": "reverse_bits" + }, + "xxhash": { + "type": "xxhash" } }, "tables": { @@ -22,6 +25,14 @@ } ] }, + "twopc_fuzzer_multi": { + "column_vindexes": [ + { + "column": "id", + "name": "xxhash" + } + ] + }, "twopc_t1": { "column_vindexes": [ { diff --git a/go/test/endtoend/transaction/twopc/main_test.go b/go/test/endtoend/transaction/twopc/main_test.go index 2f27198fd2e..631b29647c9 100644 --- a/go/test/endtoend/transaction/twopc/main_test.go +++ b/go/test/endtoend/transaction/twopc/main_test.go @@ -143,6 +143,9 @@ func cleanup(t *testing.T) { cluster.PanicHandler(t) twopcutil.ClearOutTable(t, vtParams, "twopc_user") twopcutil.ClearOutTable(t, vtParams, "twopc_t1") + twopcutil.ClearOutTable(t, vtParams, "twopc_lookup") + twopcutil.ClearOutTable(t, vtParams, "lookup_unique") + twopcutil.ClearOutTable(t, vtParams, "lookup") sm.reset() } diff --git a/go/test/endtoend/transaction/twopc/schema.sql b/go/test/endtoend/transaction/twopc/schema.sql index 7c289a03c2a..aff839eabe9 100644 --- a/go/test/endtoend/transaction/twopc/schema.sql +++ b/go/test/endtoend/transaction/twopc/schema.sql @@ -18,4 +18,27 @@ create table twopc_t1 id bigint, col bigint, primary key (id) -) Engine=InnoDB; \ No newline at end of file +) Engine=InnoDB; + +create table twopc_lookup +( + id bigint, + col bigint, + col_unique bigint, + primary key (id) +) Engine=InnoDB; + +create table lookup +( + col varchar(128), + id bigint, + keyspace_id varbinary(100), + primary key (id) +) Engine = InnoDB; + +create table lookup_unique +( + col_unique varchar(128), + keyspace_id varbinary(100), + primary key (col_unique) +) Engine = InnoDB; diff --git a/go/test/endtoend/transaction/twopc/twopc_test.go b/go/test/endtoend/transaction/twopc/twopc_test.go index 033d93f8792..5a97f79a79f 100644 --- a/go/test/endtoend/transaction/twopc/twopc_test.go +++ b/go/test/endtoend/transaction/twopc/twopc_test.go @@ -1395,8 +1395,6 @@ func TestReadTransactionStatus(t *testing.T) { "insert into twopc_t1(id, col) values(6, 4)", "insert into twopc_t1(id, col) values(9, 4)", }) - // Allow enough time for the commit to have started. - time.Sleep(1 * time.Second) // Create a tablet manager client and use it to read the transaction state. tmc := grpctmclient.NewClient() @@ -1405,12 +1403,24 @@ func TestReadTransactionStatus(t *testing.T) { defer cancel() primaryTablet := getTablet(clusterInstance.Keyspaces[0].Shards[2].FindPrimaryTablet().GrpcPort) + // Wait for the transaction to show up in the unresolved list. var unresTransaction *querypb.TransactionMetadata - for _, shard := range clusterInstance.Keyspaces[0].Shards { - urtRes, err := tmc.GetUnresolvedTransactions(ctx, getTablet(shard.FindPrimaryTablet().GrpcPort), 1) - require.NoError(t, err) - if len(urtRes) > 0 { - unresTransaction = urtRes[0] + timeout := time.After(10 * time.Second) + for { + for _, shard := range clusterInstance.Keyspaces[0].Shards { + urtRes, err := tmc.GetUnresolvedTransactions(ctx, getTablet(shard.FindPrimaryTablet().GrpcPort), 1) + require.NoError(t, err) + if len(urtRes) > 0 { + unresTransaction = urtRes[0] + } + } + if unresTransaction != nil { + break + } + select { + case <-timeout: + require.Fail(t, "timed out waiting for unresolved transaction") + default: } } require.NotNil(t, unresTransaction) @@ -1439,6 +1449,228 @@ func TestReadTransactionStatus(t *testing.T) { wg.Wait() } +// TestVindexes tests that different vindexes work well with two-phase commit. +func TestVindexes(t *testing.T) { + testcases := []struct { + name string + initQueries []string + testQueries []string + logExpected map[string][]string + }{ + { + name: "Lookup Single Update", + initQueries: []string{ + "insert into twopc_lookup(id, col, col_unique) values(4, 4, 6)", + "insert into twopc_lookup(id, col, col_unique) values(6, 4, 9)", + "insert into twopc_lookup(id, col, col_unique) values(9, 4, 4)", + }, + testQueries: []string{ + "begin", + "update twopc_lookup set col = 9 where col_unique = 9", + "commit", + }, + logExpected: map[string][]string{ + "ks.redo_statement:80-": { + "insert:[VARCHAR(\"dtid-3\") INT64(1) BLOB(\"delete from lookup where col = 4 and id = 6 and keyspace_id = _binary'`\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0' limit 10001\")]", + "insert:[VARCHAR(\"dtid-3\") INT64(2) BLOB(\"insert into lookup(col, id, keyspace_id) values (9, 6, _binary'`\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0')\")]", + "delete:[VARCHAR(\"dtid-3\") INT64(1) BLOB(\"delete from lookup where col = 4 and id = 6 and keyspace_id = _binary'`\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0' limit 10001\")]", + "delete:[VARCHAR(\"dtid-3\") INT64(2) BLOB(\"insert into lookup(col, id, keyspace_id) values (9, 6, _binary'`\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0')\")]", + }, + "ks.twopc_lookup:40-80": { + "update:[INT64(6) INT64(9) INT64(9)]", + }, + "ks.lookup:80-": { + "delete:[VARCHAR(\"4\") INT64(6) VARBINARY(\"`\\x00\\x00\\x00\\x00\\x00\\x00\\x00\")]", + "insert:[VARCHAR(\"9\") INT64(6) VARBINARY(\"`\\x00\\x00\\x00\\x00\\x00\\x00\\x00\")]", + }, + }, + }, + { + name: "Lookup-Unique Single Update", + initQueries: []string{ + "insert into twopc_lookup(id, col, col_unique) values(4, 4, 6)", + "insert into twopc_lookup(id, col, col_unique) values(6, 4, 9)", + "insert into twopc_lookup(id, col, col_unique) values(9, 4, 4)", + }, + testQueries: []string{ + "begin", + "update twopc_lookup set col_unique = 20 where col_unique = 9", + "commit", + }, + logExpected: map[string][]string{ + "ks.redo_statement:80-": { + "insert:[VARCHAR(\"dtid-3\") INT64(1) BLOB(\"delete from lookup_unique where col_unique = 9 and keyspace_id = _binary'`\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0' limit 10001\")]", + "insert:[VARCHAR(\"dtid-3\") INT64(2) BLOB(\"insert into lookup_unique(col_unique, keyspace_id) values (20, _binary'`\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0')\")]", + "delete:[VARCHAR(\"dtid-3\") INT64(1) BLOB(\"delete from lookup_unique where col_unique = 9 and keyspace_id = _binary'`\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0' limit 10001\")]", + "delete:[VARCHAR(\"dtid-3\") INT64(2) BLOB(\"insert into lookup_unique(col_unique, keyspace_id) values (20, _binary'`\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0')\")]", + }, + "ks.twopc_lookup:40-80": { + "update:[INT64(6) INT64(4) INT64(20)]", + }, + "ks.lookup_unique:80-": { + "delete:[VARCHAR(\"9\") VARBINARY(\"`\\x00\\x00\\x00\\x00\\x00\\x00\\x00\")]", + "insert:[VARCHAR(\"20\") VARBINARY(\"`\\x00\\x00\\x00\\x00\\x00\\x00\\x00\")]", + }, + }, + }, + { + name: "Lookup And Lookup-Unique Single Delete", + initQueries: []string{ + "insert into twopc_lookup(id, col, col_unique) values(4, 4, 6)", + "insert into twopc_lookup(id, col, col_unique) values(6, 4, 9)", + "insert into twopc_lookup(id, col, col_unique) values(9, 4, 4)", + }, + testQueries: []string{ + "begin", + "delete from twopc_lookup where col_unique = 9", + "commit", + }, + logExpected: map[string][]string{ + "ks.redo_statement:80-": { + "insert:[VARCHAR(\"dtid-3\") INT64(1) BLOB(\"delete from lookup where col = 4 and id = 6 and keyspace_id = _binary'`\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0' limit 10001\")]", + "insert:[VARCHAR(\"dtid-3\") INT64(2) BLOB(\"delete from lookup_unique where col_unique = 9 and keyspace_id = _binary'`\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0' limit 10001\")]", + "delete:[VARCHAR(\"dtid-3\") INT64(1) BLOB(\"delete from lookup where col = 4 and id = 6 and keyspace_id = _binary'`\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0' limit 10001\")]", + "delete:[VARCHAR(\"dtid-3\") INT64(2) BLOB(\"delete from lookup_unique where col_unique = 9 and keyspace_id = _binary'`\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0' limit 10001\")]", + }, + "ks.twopc_lookup:40-80": { + "delete:[INT64(6) INT64(4) INT64(9)]", + }, + "ks.lookup_unique:80-": { + "delete:[VARCHAR(\"9\") VARBINARY(\"`\\x00\\x00\\x00\\x00\\x00\\x00\\x00\")]", + }, + "ks.lookup:80-": { + "delete:[VARCHAR(\"4\") INT64(6) VARBINARY(\"`\\x00\\x00\\x00\\x00\\x00\\x00\\x00\")]", + }, + }, + }, + { + name: "Lookup And Lookup-Unique Single Insertion", + initQueries: []string{ + "insert into twopc_lookup(id, col, col_unique) values(4, 4, 6)", + "insert into twopc_lookup(id, col, col_unique) values(6, 4, 9)", + "insert into twopc_lookup(id, col, col_unique) values(9, 4, 4)", + }, + testQueries: []string{ + "begin", + "insert into twopc_lookup(id, col, col_unique) values(20, 4, 22)", + "commit", + }, + logExpected: map[string][]string{ + "ks.redo_statement:80-": { + "insert:[VARCHAR(\"dtid-3\") INT64(1) BLOB(\"insert into lookup(col, id, keyspace_id) values (4, 20, _binary'(\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0')\")]", + "delete:[VARCHAR(\"dtid-3\") INT64(1) BLOB(\"insert into lookup(col, id, keyspace_id) values (4, 20, _binary'(\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0')\")]", + }, + "ks.lookup:80-": { + "insert:[VARCHAR(\"4\") INT64(20) VARBINARY(\"(\\x00\\x00\\x00\\x00\\x00\\x00\\x00\")]", + }, + "ks.lookup_unique:-40": { + "insert:[VARCHAR(\"22\") VARBINARY(\"(\\x00\\x00\\x00\\x00\\x00\\x00\\x00\")]", + }, + "ks.twopc_lookup:-40": { + "insert:[INT64(20) INT64(4) INT64(22)]", + }, + }, + }, + { + name: "Lookup And Lookup-Unique Mix", + initQueries: []string{ + "insert into twopc_lookup(id, col, col_unique) values(4, 4, 6)", + "insert into twopc_lookup(id, col, col_unique) values(6, 4, 9)", + "insert into twopc_lookup(id, col, col_unique) values(9, 4, 4)", + }, + testQueries: []string{ + "begin", + "insert into twopc_lookup(id, col, col_unique) values(20, 4, 22)", + "update twopc_lookup set col = 9 where col_unique = 9", + "delete from twopc_lookup where id = 9", + "commit", + }, + logExpected: map[string][]string{ + "ks.redo_statement:80-": { + "insert:[VARCHAR(\"dtid-3\") INT64(1) BLOB(\"insert into lookup(col, id, keyspace_id) values (4, 20, _binary'(\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0')\")]", + "insert:[VARCHAR(\"dtid-3\") INT64(2) BLOB(\"delete from lookup where col = 4 and id = 6 and keyspace_id = _binary'`\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0' limit 10001\")]", + "insert:[VARCHAR(\"dtid-3\") INT64(3) BLOB(\"insert into lookup(col, id, keyspace_id) values (9, 6, _binary'`\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0')\")]", + "insert:[VARCHAR(\"dtid-3\") INT64(4) BLOB(\"delete from lookup where col = 4 and id = 9 and keyspace_id = _binary'\\x90\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0' limit 10001\")]", + "insert:[VARCHAR(\"dtid-3\") INT64(5) BLOB(\"delete from lookup_unique where col_unique = 4 and keyspace_id = _binary'\\x90\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0' limit 10001\")]", + "insert:[VARCHAR(\"dtid-3\") INT64(6) BLOB(\"delete from twopc_lookup where id = 9 limit 10001 /* INT64 */\")]", + "delete:[VARCHAR(\"dtid-3\") INT64(1) BLOB(\"insert into lookup(col, id, keyspace_id) values (4, 20, _binary'(\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0')\")]", + "delete:[VARCHAR(\"dtid-3\") INT64(2) BLOB(\"delete from lookup where col = 4 and id = 6 and keyspace_id = _binary'`\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0' limit 10001\")]", + "delete:[VARCHAR(\"dtid-3\") INT64(3) BLOB(\"insert into lookup(col, id, keyspace_id) values (9, 6, _binary'`\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0')\")]", + "delete:[VARCHAR(\"dtid-3\") INT64(4) BLOB(\"delete from lookup where col = 4 and id = 9 and keyspace_id = _binary'\\x90\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0' limit 10001\")]", + "delete:[VARCHAR(\"dtid-3\") INT64(5) BLOB(\"delete from lookup_unique where col_unique = 4 and keyspace_id = _binary'\\x90\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0' limit 10001\")]", + "delete:[VARCHAR(\"dtid-3\") INT64(6) BLOB(\"delete from twopc_lookup where id = 9 limit 10001 /* INT64 */\")]", + }, + "ks.redo_statement:40-80": { + "insert:[VARCHAR(\"dtid-3\") INT64(1) BLOB(\"update twopc_lookup set col = 9 where col_unique = 9 limit 10001 /* INT64 */\")]", + "delete:[VARCHAR(\"dtid-3\") INT64(1) BLOB(\"update twopc_lookup set col = 9 where col_unique = 9 limit 10001 /* INT64 */\")]", + }, + "ks.twopc_lookup:-40": { + "insert:[INT64(20) INT64(4) INT64(22)]", + }, + "ks.twopc_lookup:40-80": { + "update:[INT64(6) INT64(9) INT64(9)]", + }, + "ks.twopc_lookup:80-": { + "delete:[INT64(9) INT64(4) INT64(4)]", + }, + "ks.lookup_unique:-40": { + "insert:[VARCHAR(\"22\") VARBINARY(\"(\\x00\\x00\\x00\\x00\\x00\\x00\\x00\")]", + }, + "ks.lookup_unique:80-": { + "delete:[VARCHAR(\"4\") VARBINARY(\"\\x90\\x00\\x00\\x00\\x00\\x00\\x00\\x00\")]", + }, + "ks.lookup:80-": { + "insert:[VARCHAR(\"4\") INT64(20) VARBINARY(\"(\\x00\\x00\\x00\\x00\\x00\\x00\\x00\")]", + "delete:[VARCHAR(\"4\") INT64(6) VARBINARY(\"`\\x00\\x00\\x00\\x00\\x00\\x00\\x00\")]", + "insert:[VARCHAR(\"9\") INT64(6) VARBINARY(\"`\\x00\\x00\\x00\\x00\\x00\\x00\\x00\")]", + "delete:[VARCHAR(\"4\") INT64(9) VARBINARY(\"\\x90\\x00\\x00\\x00\\x00\\x00\\x00\\x00\")]", + }, + }, + }, + } + + for _, tt := range testcases { + t.Run(tt.name, func(t *testing.T) { + defer cleanup(t) + + vtgateConn, err := cluster.DialVTGate(context.Background(), t.Name(), vtgateGrpcAddress, "dt_user", "") + require.NoError(t, err) + defer vtgateConn.Close() + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + ch := make(chan *binlogdatapb.VEvent) + runVStream(t, ctx, ch, vtgateConn) + + conn := vtgateConn.Session("", nil) + qCtx, cancel := context.WithCancel(context.Background()) + defer cancel() + + // initial insert + for _, query := range tt.initQueries { + execute(qCtx, t, conn, query) + } + + // ignore initial change + tableMap := make(map[string][]*querypb.Field) + dtMap := make(map[string]string) + _ = retrieveTransitionsWithTimeout(t, ch, tableMap, dtMap, 2*time.Second) + + // Insert into multiple shards + for _, query := range tt.testQueries { + execute(qCtx, t, conn, query) + } + + // Below check ensures that the transaction is resolved by the resolver on receiving unresolved transaction signal from MM. + logTable := retrieveTransitionsWithTimeout(t, ch, tableMap, dtMap, 2*time.Second) + for key, val := range tt.logExpected { + assert.EqualValues(t, val, logTable[key], key) + } + }) + } +} + func getTablet(tabletGrpcPort int) *tabletpb.Tablet { portMap := make(map[string]int32) portMap["grpc"] = int32(tabletGrpcPort) diff --git a/go/test/endtoend/transaction/twopc/vschema.json b/go/test/endtoend/transaction/twopc/vschema.json index bca58b05c1e..0c22f40d54b 100644 --- a/go/test/endtoend/transaction/twopc/vschema.json +++ b/go/test/endtoend/transaction/twopc/vschema.json @@ -6,6 +6,24 @@ }, "reverse_bits": { "type": "reverse_bits" + }, + "lookup_vdx": { + "type": "lookup", + "params": { + "table": "lookup", + "from": "col,id", + "to": "keyspace_id" + }, + "owner": "twopc_lookup" + }, + "lookup_unique_vdx": { + "type": "lookup_unique", + "params": { + "table": "lookup_unique", + "from": "col_unique", + "to": "keyspace_id" + }, + "owner": "twopc_lookup" } }, "tables": { @@ -32,6 +50,41 @@ "name": "reverse_bits" } ] + }, + "twopc_lookup": { + "column_vindexes": [ + { + "column": "id", + "name": "reverse_bits" + }, + { + "columns": [ + "col", + "id" + ], + "name": "lookup_vdx" + }, + { + "column": "col_unique", + "name": "lookup_unique_vdx" + } + ] + }, + "lookup": { + "column_vindexes": [ + { + "column": "col", + "name": "xxhash" + } + ] + }, + "lookup_unique": { + "column_vindexes": [ + { + "column": "col_unique", + "name": "xxhash" + } + ] } } } \ No newline at end of file diff --git a/go/test/endtoend/vreplication/cluster_test.go b/go/test/endtoend/vreplication/cluster_test.go index 119843651bc..dc5a72e5e88 100644 --- a/go/test/endtoend/vreplication/cluster_test.go +++ b/go/test/endtoend/vreplication/cluster_test.go @@ -39,7 +39,6 @@ import ( "vitess.io/vitess/go/vt/mysqlctl" "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" - vttablet "vitess.io/vitess/go/vt/vttablet/common" vtctldatapb "vitess.io/vitess/go/vt/proto/vtctldata" ) @@ -101,18 +100,6 @@ func (cc *ClusterConfig) enableGTIDCompression() func() { } } -// setAllVTTabletExperimentalFlags sets all the experimental flags for vttablet and returns a function -// that can be used to reset them in a defer. -func setAllVTTabletExperimentalFlags() func() { - experimentalArgs := fmt.Sprintf("--vreplication_experimental_flags=%d", - vttablet.VReplicationExperimentalFlagAllowNoBlobBinlogRowImage|vttablet.VReplicationExperimentalFlagOptimizeInserts|vttablet.VReplicationExperimentalFlagVPlayerBatching) - oldArgs := extraVTTabletArgs - extraVTTabletArgs = append(extraVTTabletArgs, experimentalArgs) - return func() { - extraVTTabletArgs = oldArgs - } -} - // VitessCluster represents all components within the test cluster type VitessCluster struct { t *testing.T diff --git a/go/test/endtoend/vreplication/fk_test.go b/go/test/endtoend/vreplication/fk_test.go index 34881cbcd1a..f977d5a74cd 100644 --- a/go/test/endtoend/vreplication/fk_test.go +++ b/go/test/endtoend/vreplication/fk_test.go @@ -29,7 +29,6 @@ import ( "vitess.io/vitess/go/mysql" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/log" - vttablet "vitess.io/vitess/go/vt/vttablet/common" binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" ) @@ -43,9 +42,6 @@ func TestFKWorkflow(t *testing.T) { extraVTTabletArgs = []string{ // Ensure that there are multiple copy phase cycles per table. "--vstream_packet_size=256", - // Test VPlayer batching mode. - fmt.Sprintf("--vreplication_experimental_flags=%d", - vttablet.VReplicationExperimentalFlagAllowNoBlobBinlogRowImage|vttablet.VReplicationExperimentalFlagOptimizeInserts|vttablet.VReplicationExperimentalFlagVPlayerBatching), } defer func() { extraVTTabletArgs = nil }() diff --git a/go/test/endtoend/vreplication/vdiff2_test.go b/go/test/endtoend/vreplication/vdiff2_test.go index aaf4cae5375..612ba00236b 100644 --- a/go/test/endtoend/vreplication/vdiff2_test.go +++ b/go/test/endtoend/vreplication/vdiff2_test.go @@ -36,7 +36,6 @@ import ( "vitess.io/vitess/go/test/endtoend/cluster" "vitess.io/vitess/go/vt/log" "vitess.io/vitess/go/vt/sqlparser" - vttablet "vitess.io/vitess/go/vt/vttablet/common" binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" tabletmanagerdatapb "vitess.io/vitess/go/vt/proto/tabletmanagerdata" @@ -140,9 +139,6 @@ func TestVDiff2(t *testing.T) { extraVTTabletArgs = []string{ // This forces us to use multiple vstream packets even with small test tables. "--vstream_packet_size=1", - // Test VPlayer batching mode. - fmt.Sprintf("--vreplication_experimental_flags=%d", - vttablet.VReplicationExperimentalFlagAllowNoBlobBinlogRowImage|vttablet.VReplicationExperimentalFlagOptimizeInserts|vttablet.VReplicationExperimentalFlagVPlayerBatching), } vc = NewVitessCluster(t, &clusterOptions{cells: strings.Split(cellNames, ",")}) diff --git a/go/test/endtoend/vreplication/vdiff_helper_test.go b/go/test/endtoend/vreplication/vdiff_helper_test.go index 561edfe8b7e..fd223d78082 100644 --- a/go/test/endtoend/vreplication/vdiff_helper_test.go +++ b/go/test/endtoend/vreplication/vdiff_helper_test.go @@ -35,7 +35,7 @@ import ( ) const ( - vdiffTimeout = 120 * time.Second // We can leverage auto retry on error with this longer-than-usual timeout + vdiffTimeout = 180 * time.Second // We can leverage auto retry on error with this longer-than-usual timeout vdiffRetryTimeout = 30 * time.Second vdiffStatusCheckInterval = 5 * time.Second vdiffRetryInterval = 5 * time.Second @@ -71,7 +71,8 @@ func doVtctlclientVDiff(t *testing.T, keyspace, workflow, cells string, want *ex ksWorkflow := fmt.Sprintf("%s.%s", keyspace, workflow) t.Run(fmt.Sprintf("vtctlclient vdiff %s", ksWorkflow), func(t *testing.T) { // update-table-stats is needed in order to test progress reports. - uuid, _ := performVDiff2Action(t, true, ksWorkflow, cells, "create", "", false, "--auto-retry", "--update-table-stats") + uuid, _ := performVDiff2Action(t, true, ksWorkflow, cells, "create", "", false, "--auto-retry", + "--update-table-stats", fmt.Sprintf("--filtered_replication_wait_time=%v", vdiffTimeout/2)) info := waitForVDiff2ToComplete(t, true, ksWorkflow, cells, uuid, time.Time{}) require.NotNil(t, info) require.Equal(t, workflow, info.Workflow) @@ -164,7 +165,7 @@ func doVtctldclientVDiff(t *testing.T, keyspace, workflow, cells string, want *e ksWorkflow := fmt.Sprintf("%s.%s", keyspace, workflow) t.Run(fmt.Sprintf("vtctldclient vdiff %s", ksWorkflow), func(t *testing.T) { // update-table-stats is needed in order to test progress reports. - flags := []string{"--auto-retry", "--update-table-stats"} + flags := []string{"--auto-retry", "--update-table-stats", fmt.Sprintf("--filtered-replication-wait-time=%v", vdiffTimeout/2)} if len(extraFlags) > 0 { flags = append(flags, extraFlags...) } diff --git a/go/test/endtoend/vreplication/vreplication_test.go b/go/test/endtoend/vreplication/vreplication_test.go index 04a5eabc33b..d3193298a0c 100644 --- a/go/test/endtoend/vreplication/vreplication_test.go +++ b/go/test/endtoend/vreplication/vreplication_test.go @@ -293,7 +293,6 @@ func TestVreplicationCopyThrottling(t *testing.T) { } func TestBasicVreplicationWorkflow(t *testing.T) { - defer setAllVTTabletExperimentalFlags() sourceKsOpts["DBTypeVersion"] = "mysql-8.0" targetKsOpts["DBTypeVersion"] = "mysql-8.0" testBasicVreplicationWorkflow(t, "noblob") @@ -595,8 +594,6 @@ func TestCellAliasVreplicationWorkflow(t *testing.T) { cells := []string{"zone1", "zone2"} resetCompression := mainClusterConfig.enableGTIDCompression() defer resetCompression() - resetExperimentalFlags := setAllVTTabletExperimentalFlags() - defer resetExperimentalFlags() vc = NewVitessCluster(t, &clusterOptions{cells: cells}) defer vc.TearDown() diff --git a/go/test/endtoend/vtorc/api/api_test.go b/go/test/endtoend/vtorc/api/api_test.go index 670e8c803fa..638ea5fa72e 100644 --- a/go/test/endtoend/vtorc/api/api_test.go +++ b/go/test/endtoend/vtorc/api/api_test.go @@ -35,8 +35,7 @@ import ( func TestAPIEndpoints(t *testing.T) { defer cluster.PanicHandler(t) utils.SetupVttabletsAndVTOrcs(t, clusterInfo, 2, 1, nil, cluster.VTOrcConfiguration{ - PreventCrossDataCenterPrimaryFailover: true, - RecoveryPeriodBlockSeconds: 5, + PreventCrossCellFailover: true, }, 1, "") keyspace := &clusterInfo.ClusterInstance.Keyspaces[0] shard0 := &keyspace.Shards[0] diff --git a/go/test/endtoend/vtorc/api/config_test.go b/go/test/endtoend/vtorc/api/config_test.go new file mode 100644 index 00000000000..71cc6291be7 --- /dev/null +++ b/go/test/endtoend/vtorc/api/config_test.go @@ -0,0 +1,204 @@ +/* +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 api + +import ( + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "vitess.io/vitess/go/test/endtoend/cluster" + "vitess.io/vitess/go/test/endtoend/vtorc/utils" +) + +// TestDynamicConfigs tests the dyanamic configurations that VTOrc offers. +func TestDynamicConfigs(t *testing.T) { + defer cluster.PanicHandler(t) + utils.SetupVttabletsAndVTOrcs(t, clusterInfo, 2, 1, nil, cluster.VTOrcConfiguration{}, 1, "") + vtorc := clusterInfo.ClusterInstance.VTOrcProcesses[0] + + // Restart VTOrc without any flag overrides so that all the configurations can be tested. + err := vtorc.TearDown() + require.NoError(t, err) + vtorc.Config = cluster.VTOrcConfiguration{} + vtorc.NoOverride = true + err = vtorc.Setup() + require.NoError(t, err) + + // Call API with retry to ensure VTOrc is up + status, resp := utils.MakeAPICallRetry(t, vtorc, "/debug/health", func(code int, response string) bool { + return code != 200 + }) + // Verify when VTOrc is healthy, it has also run the first discovery. + assert.Equal(t, 200, status) + assert.Contains(t, resp, `"Healthy": true,`) + + t.Run("InstancePollTime", func(t *testing.T) { + // Get configuration and verify the output. + waitForConfig(t, vtorc, `"instance-poll-time": 5000000000`) + // Update configuration and verify the output. + vtorc.Config.InstancePollTime = "10h" + err := vtorc.RewriteConfiguration() + assert.NoError(t, err) + // Wait until the config has been updated and seen. + waitForConfig(t, vtorc, `"instance-poll-time": "10h"`) + }) + + t.Run("PreventCrossCellFailover", func(t *testing.T) { + // Get configuration and verify the output. + waitForConfig(t, vtorc, `"prevent-cross-cell-failover": false`) + // Update configuration and verify the output. + vtorc.Config.PreventCrossCellFailover = true + err := vtorc.RewriteConfiguration() + assert.NoError(t, err) + // Wait until the config has been updated and seen. + waitForConfig(t, vtorc, `"prevent-cross-cell-failover": true`) + }) + + t.Run("SnapshotTopologyInterval", func(t *testing.T) { + // Get configuration and verify the output. + waitForConfig(t, vtorc, `"snapshot-topology-interval": 0`) + // Update configuration and verify the output. + vtorc.Config.SnapshotTopologyInterval = "10h" + err := vtorc.RewriteConfiguration() + assert.NoError(t, err) + // Wait until the config has been updated and seen. + waitForConfig(t, vtorc, `"snapshot-topology-interval": "10h"`) + }) + + t.Run("ReasonableReplicationLag", func(t *testing.T) { + // Get configuration and verify the output. + waitForConfig(t, vtorc, `"reasonable-replication-lag": 10000000000`) + // Update configuration and verify the output. + vtorc.Config.ReasonableReplicationLag = "10h" + err := vtorc.RewriteConfiguration() + assert.NoError(t, err) + // Wait until the config has been updated and seen. + waitForConfig(t, vtorc, `"reasonable-replication-lag": "10h"`) + }) + + t.Run("AuditToBackend", func(t *testing.T) { + // Get configuration and verify the output. + waitForConfig(t, vtorc, `"audit-to-backend": false`) + // Update configuration and verify the output. + vtorc.Config.AuditToBackend = true + err := vtorc.RewriteConfiguration() + assert.NoError(t, err) + // Wait until the config has been updated and seen. + waitForConfig(t, vtorc, `"audit-to-backend": true`) + }) + + t.Run("AuditToSyslog", func(t *testing.T) { + // Get configuration and verify the output. + waitForConfig(t, vtorc, `"audit-to-syslog": false`) + // Update configuration and verify the output. + vtorc.Config.AuditToSyslog = true + err := vtorc.RewriteConfiguration() + assert.NoError(t, err) + // Wait until the config has been updated and seen. + waitForConfig(t, vtorc, `"audit-to-syslog": true`) + }) + + t.Run("AuditPurgeDuration", func(t *testing.T) { + // Get configuration and verify the output. + waitForConfig(t, vtorc, `"audit-purge-duration": 604800000000000`) + // Update configuration and verify the output. + vtorc.Config.AuditPurgeDuration = "10h" + err := vtorc.RewriteConfiguration() + assert.NoError(t, err) + // Wait until the config has been updated and seen. + waitForConfig(t, vtorc, `"audit-purge-duration": "10h"`) + }) + + t.Run("WaitReplicasTimeout", func(t *testing.T) { + // Get configuration and verify the output. + waitForConfig(t, vtorc, `"wait-replicas-timeout": 30000000000`) + // Update configuration and verify the output. + vtorc.Config.WaitReplicasTimeout = "10h" + err := vtorc.RewriteConfiguration() + assert.NoError(t, err) + // Wait until the config has been updated and seen. + waitForConfig(t, vtorc, `"wait-replicas-timeout": "10h"`) + }) + + t.Run("TolerableReplicationLag", func(t *testing.T) { + // Get configuration and verify the output. + waitForConfig(t, vtorc, `"tolerable-replication-lag": 0`) + // Update configuration and verify the output. + vtorc.Config.TolerableReplicationLag = "10h" + err := vtorc.RewriteConfiguration() + assert.NoError(t, err) + // Wait until the config has been updated and seen. + waitForConfig(t, vtorc, `"tolerable-replication-lag": "10h"`) + }) + + t.Run("TopoInformationRefreshDuration", func(t *testing.T) { + // Get configuration and verify the output. + waitForConfig(t, vtorc, `"topo-information-refresh-duration": 15000000000`) + // Update configuration and verify the output. + vtorc.Config.TopoInformationRefreshDuration = "10h" + err := vtorc.RewriteConfiguration() + assert.NoError(t, err) + // Wait until the config has been updated and seen. + waitForConfig(t, vtorc, `"topo-information-refresh-duration": "10h"`) + }) + + t.Run("RecoveryPollDuration", func(t *testing.T) { + // Get configuration and verify the output. + waitForConfig(t, vtorc, `"recovery-poll-duration": 1000000000`) + // Update configuration and verify the output. + vtorc.Config.RecoveryPollDuration = "10h" + err := vtorc.RewriteConfiguration() + assert.NoError(t, err) + // Wait until the config has been updated and seen. + waitForConfig(t, vtorc, `"recovery-poll-duration": "10h"`) + }) + + t.Run("AllowEmergencyReparent", func(t *testing.T) { + // Get configuration and verify the output. + waitForConfig(t, vtorc, `"allow-emergency-reparent": true`) + // Update configuration and verify the output. + vtorc.Config.AllowEmergencyReparent = "false" + err := vtorc.RewriteConfiguration() + assert.NoError(t, err) + // Wait until the config has been updated and seen. + waitForConfig(t, vtorc, `"allow-emergency-reparent": "false"`) + }) + + t.Run("ChangeTabletsWithErrantGtidToDrained", func(t *testing.T) { + // Get configuration and verify the output. + waitForConfig(t, vtorc, `"change-tablets-with-errant-gtid-to-drained": false`) + // Update configuration and verify the output. + vtorc.Config.ChangeTabletsWithErrantGtidToDrained = true + err := vtorc.RewriteConfiguration() + assert.NoError(t, err) + // Wait until the config has been updated and seen. + waitForConfig(t, vtorc, `"change-tablets-with-errant-gtid-to-drained": true`) + }) +} + +// waitForConfig waits for the expectedConfig to be present in the VTOrc configuration. +func waitForConfig(t *testing.T, vtorc *cluster.VTOrcProcess, expectedConfig string) { + t.Helper() + status, _ := utils.MakeAPICallRetry(t, vtorc, "/api/config", func(_ int, response string) bool { + return !strings.Contains(response, expectedConfig) + }) + require.EqualValues(t, 200, status) +} diff --git a/go/test/endtoend/vtorc/general/vtorc_test.go b/go/test/endtoend/vtorc/general/vtorc_test.go index 329601deb0c..9cc931897bf 100644 --- a/go/test/endtoend/vtorc/general/vtorc_test.go +++ b/go/test/endtoend/vtorc/general/vtorc_test.go @@ -42,7 +42,7 @@ func TestPrimaryElection(t *testing.T) { defer utils.PrintVTOrcLogsOnFailure(t, clusterInfo.ClusterInstance) defer cluster.PanicHandler(t) utils.SetupVttabletsAndVTOrcs(t, clusterInfo, 2, 1, nil, cluster.VTOrcConfiguration{ - PreventCrossDataCenterPrimaryFailover: true, + PreventCrossCellFailover: true, }, 2, "") keyspace := &clusterInfo.ClusterInstance.Keyspaces[0] shard0 := &keyspace.Shards[0] @@ -62,6 +62,64 @@ func TestPrimaryElection(t *testing.T) { require.Len(t, res.Rows, 1, "There should only be 1 primary tablet which was elected") } +// TestErrantGTIDOnPreviousPrimary tests that VTOrc is able to detect errant GTIDs on a previously demoted primary +// if it has an errant GTID. +func TestErrantGTIDOnPreviousPrimary(t *testing.T) { + defer utils.PrintVTOrcLogsOnFailure(t, clusterInfo.ClusterInstance) + defer cluster.PanicHandler(t) + utils.SetupVttabletsAndVTOrcs(t, clusterInfo, 3, 0, []string{"--change-tablets-with-errant-gtid-to-drained"}, cluster.VTOrcConfiguration{}, 1, "") + keyspace := &clusterInfo.ClusterInstance.Keyspaces[0] + shard0 := &keyspace.Shards[0] + + // find primary from topo + curPrimary := utils.ShardPrimaryTablet(t, clusterInfo, keyspace, shard0) + assert.NotNil(t, curPrimary, "should have elected a primary") + vtOrcProcess := clusterInfo.ClusterInstance.VTOrcProcesses[0] + utils.WaitForSuccessfulRecoveryCount(t, vtOrcProcess, logic.ElectNewPrimaryRecoveryName, 1) + utils.WaitForSuccessfulPRSCount(t, vtOrcProcess, keyspace.Name, shard0.Name, 1) + + var replica, otherReplica *cluster.Vttablet + for _, tablet := range shard0.Vttablets { + // we know we have only two tablets, so the "other" one must be the new primary + if tablet.Alias != curPrimary.Alias { + if replica == nil { + replica = tablet + } else { + otherReplica = tablet + } + } + } + require.NotNil(t, replica, "should be able to find a replica") + require.NotNil(t, otherReplica, "should be able to find 2nd replica") + utils.CheckReplication(t, clusterInfo, curPrimary, []*cluster.Vttablet{replica, otherReplica}, 15*time.Second) + + // Disable global recoveries for the cluster. + vtOrcProcess.DisableGlobalRecoveries(t) + + // Run PRS to promote a different replica. + output, err := clusterInfo.ClusterInstance.VtctldClientProcess.ExecuteCommandWithOutput( + "PlannedReparentShard", + fmt.Sprintf("%s/%s", keyspace.Name, shard0.Name), + "--new-primary", replica.Alias) + require.NoError(t, err, "error in PlannedReparentShard output - %s", output) + + // Stop replicatin on the previous primary to simulate it not reparenting properly. + // Also insert an errant GTID on the previous primary. + err = utils.RunSQLs(t, []string{ + "STOP REPLICA", + "RESET REPLICA ALL", + "set global read_only=OFF", + "insert into vt_ks.vt_insert_test(id, msg) values (10173, 'test 178342')", + }, curPrimary, "") + require.NoError(t, err) + + // Wait for VTOrc to detect the errant GTID and change the tablet to a drained type. + vtOrcProcess.EnableGlobalRecoveries(t) + + // Wait for the tablet to be drained. + utils.WaitForTabletType(t, curPrimary, "drained") +} + // Cases to test: // 1. create cluster with 1 replica and 1 rdonly, let orc choose primary // verify rdonly is not elected, only replica @@ -70,7 +128,7 @@ func TestSingleKeyspace(t *testing.T) { defer utils.PrintVTOrcLogsOnFailure(t, clusterInfo.ClusterInstance) defer cluster.PanicHandler(t) utils.SetupVttabletsAndVTOrcs(t, clusterInfo, 1, 1, []string{"--clusters_to_watch", "ks"}, cluster.VTOrcConfiguration{ - PreventCrossDataCenterPrimaryFailover: true, + PreventCrossCellFailover: true, }, 1, "") keyspace := &clusterInfo.ClusterInstance.Keyspaces[0] shard0 := &keyspace.Shards[0] @@ -89,7 +147,7 @@ func TestKeyspaceShard(t *testing.T) { defer utils.PrintVTOrcLogsOnFailure(t, clusterInfo.ClusterInstance) defer cluster.PanicHandler(t) utils.SetupVttabletsAndVTOrcs(t, clusterInfo, 1, 1, []string{"--clusters_to_watch", "ks/0"}, cluster.VTOrcConfiguration{ - PreventCrossDataCenterPrimaryFailover: true, + PreventCrossCellFailover: true, }, 1, "") keyspace := &clusterInfo.ClusterInstance.Keyspaces[0] shard0 := &keyspace.Shards[0] @@ -111,7 +169,7 @@ func TestVTOrcRepairs(t *testing.T) { defer utils.PrintVTOrcLogsOnFailure(t, clusterInfo.ClusterInstance) defer cluster.PanicHandler(t) utils.SetupVttabletsAndVTOrcs(t, clusterInfo, 3, 0, []string{"--change-tablets-with-errant-gtid-to-drained"}, cluster.VTOrcConfiguration{ - PreventCrossDataCenterPrimaryFailover: true, + PreventCrossCellFailover: true, }, 1, "") keyspace := &clusterInfo.ClusterInstance.Keyspaces[0] shard0 := &keyspace.Shards[0] @@ -290,7 +348,7 @@ func TestRepairAfterTER(t *testing.T) { defer utils.PrintVTOrcLogsOnFailure(t, clusterInfo.ClusterInstance) defer cluster.PanicHandler(t) utils.SetupVttabletsAndVTOrcs(t, clusterInfo, 2, 0, nil, cluster.VTOrcConfiguration{ - PreventCrossDataCenterPrimaryFailover: true, + PreventCrossCellFailover: true, }, 1, "") keyspace := &clusterInfo.ClusterInstance.Keyspaces[0] shard0 := &keyspace.Shards[0] @@ -326,7 +384,7 @@ func TestSemiSync(t *testing.T) { newCluster := utils.SetupNewClusterSemiSync(t) defer utils.PrintVTOrcLogsOnFailure(t, newCluster.ClusterInstance) utils.StartVTOrcs(t, newCluster, nil, cluster.VTOrcConfiguration{ - PreventCrossDataCenterPrimaryFailover: true, + PreventCrossCellFailover: true, }, 1) defer func() { utils.StopVTOrcs(t, newCluster) @@ -424,7 +482,7 @@ func TestVTOrcWithPrs(t *testing.T) { defer utils.PrintVTOrcLogsOnFailure(t, clusterInfo.ClusterInstance) defer cluster.PanicHandler(t) utils.SetupVttabletsAndVTOrcs(t, clusterInfo, 4, 0, nil, cluster.VTOrcConfiguration{ - PreventCrossDataCenterPrimaryFailover: true, + PreventCrossCellFailover: true, }, 1, "") keyspace := &clusterInfo.ClusterInstance.Keyspaces[0] shard0 := &keyspace.Shards[0] @@ -456,8 +514,6 @@ func TestVTOrcWithPrs(t *testing.T) { "--new-primary", replica.Alias) require.NoError(t, err, "error in PlannedReparentShard output - %s", output) - time.Sleep(40 * time.Second) - // check that the replica gets promoted utils.CheckPrimaryTablet(t, clusterInfo, replica, true) // Verify that VTOrc didn't run any other recovery @@ -560,7 +616,7 @@ func TestDurabilityPolicySetLater(t *testing.T) { // Now start the vtorc instances utils.StartVTOrcs(t, newCluster, nil, cluster.VTOrcConfiguration{ - PreventCrossDataCenterPrimaryFailover: true, + PreventCrossCellFailover: true, }, 1) defer func() { utils.StopVTOrcs(t, newCluster) @@ -587,7 +643,7 @@ func TestFullStatusConnectionPooling(t *testing.T) { utils.SetupVttabletsAndVTOrcs(t, clusterInfo, 4, 0, []string{ "--tablet_manager_grpc_concurrency=1", }, cluster.VTOrcConfiguration{ - PreventCrossDataCenterPrimaryFailover: true, + PreventCrossCellFailover: true, }, 1, "") keyspace := &clusterInfo.ClusterInstance.Keyspaces[0] shard0 := &keyspace.Shards[0] diff --git a/go/test/endtoend/vtorc/primaryfailure/primary_failure_test.go b/go/test/endtoend/vtorc/primaryfailure/primary_failure_test.go index 886aa3a580a..a46e3789730 100644 --- a/go/test/endtoend/vtorc/primaryfailure/primary_failure_test.go +++ b/go/test/endtoend/vtorc/primaryfailure/primary_failure_test.go @@ -44,7 +44,7 @@ func TestDownPrimary(t *testing.T) { // If that replica is more advanced than the same-cell-replica, then we try to promote the cross-cell replica as an intermediate source. // If we don't specify a small value of --wait-replicas-timeout, then we would end up waiting for 30 seconds for the dead-primary to respond, failing this test. utils.SetupVttabletsAndVTOrcs(t, clusterInfo, 2, 1, []string{"--remote_operation_timeout=10s", "--wait-replicas-timeout=5s"}, cluster.VTOrcConfiguration{ - PreventCrossDataCenterPrimaryFailover: true, + PreventCrossCellFailover: true, }, 1, "semi_sync") keyspace := &clusterInfo.ClusterInstance.Keyspaces[0] shard0 := &keyspace.Shards[0] @@ -150,7 +150,7 @@ func TestDownPrimaryBeforeVTOrc(t *testing.T) { // Start a VTOrc instance utils.StartVTOrcs(t, clusterInfo, []string{"--remote_operation_timeout=10s"}, cluster.VTOrcConfiguration{ - PreventCrossDataCenterPrimaryFailover: true, + PreventCrossCellFailover: true, }, 1) vtOrcProcess := clusterInfo.ClusterInstance.VTOrcProcesses[0] @@ -244,7 +244,7 @@ func TestDeadPrimaryRecoversImmediately(t *testing.T) { // If that replica is more advanced than the same-cell-replica, then we try to promote the cross-cell replica as an intermediate source. // If we don't specify a small value of --wait-replicas-timeout, then we would end up waiting for 30 seconds for the dead-primary to respond, failing this test. utils.SetupVttabletsAndVTOrcs(t, clusterInfo, 2, 1, []string{"--remote_operation_timeout=10s", "--wait-replicas-timeout=5s"}, cluster.VTOrcConfiguration{ - PreventCrossDataCenterPrimaryFailover: true, + PreventCrossCellFailover: true, }, 1, "semi_sync") keyspace := &clusterInfo.ClusterInstance.Keyspaces[0] shard0 := &keyspace.Shards[0] @@ -324,7 +324,7 @@ func TestCrossDataCenterFailure(t *testing.T) { defer utils.PrintVTOrcLogsOnFailure(t, clusterInfo.ClusterInstance) defer cluster.PanicHandler(t) utils.SetupVttabletsAndVTOrcs(t, clusterInfo, 2, 1, nil, cluster.VTOrcConfiguration{ - PreventCrossDataCenterPrimaryFailover: true, + PreventCrossCellFailover: true, }, 1, "") keyspace := &clusterInfo.ClusterInstance.Keyspaces[0] shard0 := &keyspace.Shards[0] @@ -370,7 +370,7 @@ func TestCrossDataCenterFailureError(t *testing.T) { defer utils.PrintVTOrcLogsOnFailure(t, clusterInfo.ClusterInstance) defer cluster.PanicHandler(t) utils.SetupVttabletsAndVTOrcs(t, clusterInfo, 1, 1, nil, cluster.VTOrcConfiguration{ - PreventCrossDataCenterPrimaryFailover: true, + PreventCrossCellFailover: true, }, 1, "") keyspace := &clusterInfo.ClusterInstance.Keyspaces[0] shard0 := &keyspace.Shards[0] @@ -417,7 +417,7 @@ func TestLostRdonlyOnPrimaryFailure(t *testing.T) { defer utils.PrintVTOrcLogsOnFailure(t, clusterInfo.ClusterInstance) defer cluster.PanicHandler(t) utils.SetupVttabletsAndVTOrcs(t, clusterInfo, 2, 2, nil, cluster.VTOrcConfiguration{ - PreventCrossDataCenterPrimaryFailover: true, + PreventCrossCellFailover: true, }, 1, "") keyspace := &clusterInfo.ClusterInstance.Keyspaces[0] shard0 := &keyspace.Shards[0] @@ -728,8 +728,8 @@ func TestDownPrimaryPromotionRuleWithLagCrossCenter(t *testing.T) { defer utils.PrintVTOrcLogsOnFailure(t, clusterInfo.ClusterInstance) defer cluster.PanicHandler(t) utils.SetupVttabletsAndVTOrcs(t, clusterInfo, 2, 1, nil, cluster.VTOrcConfiguration{ - LockShardTimeoutSeconds: 5, - PreventCrossDataCenterPrimaryFailover: true, + LockShardTimeoutSeconds: 5, + PreventCrossCellFailover: true, }, 1, "test") keyspace := &clusterInfo.ClusterInstance.Keyspaces[0] shard0 := &keyspace.Shards[0] diff --git a/go/test/endtoend/vtorc/readtopologyinstance/main_test.go b/go/test/endtoend/vtorc/readtopologyinstance/main_test.go index fa8dc116782..823655ed785 100644 --- a/go/test/endtoend/vtorc/readtopologyinstance/main_test.go +++ b/go/test/endtoend/vtorc/readtopologyinstance/main_test.go @@ -55,8 +55,7 @@ func TestReadTopologyInstanceBufferable(t *testing.T) { "--topo_global_root", clusterInfo.ClusterInstance.VtctlProcess.TopoGlobalRoot, } servenv.ParseFlags("vtorc") - config.Config.RecoveryPeriodBlockSeconds = 1 - config.Config.InstancePollSeconds = 1 + config.SetInstancePollTime(1 * time.Second) config.MarkConfigurationLoaded() server.StartVTOrcDiscovery() diff --git a/go/test/endtoend/vtorc/utils/utils.go b/go/test/endtoend/vtorc/utils/utils.go index 680d1bfa39a..456d55518dd 100644 --- a/go/test/endtoend/vtorc/utils/utils.go +++ b/go/test/endtoend/vtorc/utils/utils.go @@ -376,7 +376,6 @@ func CheckPrimaryTablet(t *testing.T, clusterInfo *VTOrcClusterInfo, tablet *clu for { now := time.Now() if now.Sub(start) > time.Second*60 { - //log.Exitf("error") assert.FailNow(t, "failed to elect primary before timeout") } tabletInfo, err := clusterInfo.ClusterInstance.VtctldClientProcess.GetTablet(tablet.Alias) @@ -775,10 +774,10 @@ func MakeAPICallRetry(t *testing.T, vtorc *cluster.VTOrcProcess, url string, ret for { select { case <-timeout: - t.Fatal("timed out waiting for api to work") + t.Fatalf("timed out waiting for api to work. Last response - %s", response) return default: - status, response, _ := MakeAPICall(t, vtorc, url) + status, response, _ = MakeAPICall(t, vtorc, url) if retry(status, response) { time.Sleep(1 * time.Second) break diff --git a/go/test/vschemawrapper/vschema_wrapper.go b/go/test/vschemawrapper/vschema_wrapper.go index a1b87f5569c..3f9f072afc6 100644 --- a/go/test/vschemawrapper/vschema_wrapper.go +++ b/go/test/vschemawrapper/vschema_wrapper.go @@ -33,6 +33,7 @@ import ( "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vtgate/engine" + econtext "vitess.io/vitess/go/vt/vtgate/executorcontext" "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" "vitess.io/vitess/go/vt/vtgate/semantics" "vitess.io/vitess/go/vt/vtgate/vindexes" @@ -40,7 +41,10 @@ import ( var _ plancontext.VSchema = (*VSchemaWrapper)(nil) +// VSchemaWrapper is a wrapper around VSchema that implements the ContextVSchema interface. +// It is used in tests to provide a VSchema implementation. type VSchemaWrapper struct { + Vcursor *econtext.VCursorImpl V *vindexes.VSchema Keyspace *vindexes.Keyspace TabletType_ topodatapb.TabletType @@ -53,6 +57,30 @@ type VSchemaWrapper struct { Env *vtenv.Environment } +func NewVschemaWrapper( + env *vtenv.Environment, + vschema *vindexes.VSchema, + builder func(string, plancontext.VSchema, string) (*engine.Plan, error), +) (*VSchemaWrapper, error) { + ss := econtext.NewAutocommitSession(&vtgatepb.Session{}) + vcursor, err := econtext.NewVCursorImpl(ss, sqlparser.MarginComments{}, nil, nil, nil, vschema, nil, nil, nil, econtext.VCursorConfig{ + Collation: env.CollationEnv().DefaultConnectionCharset(), + DefaultTabletType: topodatapb.TabletType_PRIMARY, + SetVarEnabled: true, + }) + if err != nil { + return nil, err + } + return &VSchemaWrapper{ + Env: env, + V: vschema, + Vcursor: vcursor, + TestBuilder: builder, + TabletType_: topodatapb.TabletType_PRIMARY, + SysVarEnabled: true, + }, nil +} + func (vw *VSchemaWrapper) GetPrepareData(stmtName string) *vtgatepb.PrepareData { switch stmtName { case "prep_one_param": @@ -244,34 +272,7 @@ func (vw *VSchemaWrapper) FindView(tab sqlparser.TableName) sqlparser.SelectStat } func (vw *VSchemaWrapper) FindTableOrVindex(tab sqlparser.TableName) (*vindexes.Table, vindexes.Vindex, string, topodatapb.TabletType, key.Destination, error) { - if tab.Qualifier.IsEmpty() && tab.Name.String() == "dual" { - ksName := vw.getActualKeyspace() - var ks *vindexes.Keyspace - if ksName == "" { - ks = vw.getfirstKeyspace() - ksName = ks.Name - } else { - ks = vw.V.Keyspaces[ksName].Keyspace - } - tbl := &vindexes.Table{ - Name: sqlparser.NewIdentifierCS("dual"), - Keyspace: ks, - Type: vindexes.TypeReference, - } - return tbl, nil, ksName, topodatapb.TabletType_PRIMARY, nil, nil - } - destKeyspace, destTabletType, destTarget, err := topoproto.ParseDestination(tab.Qualifier.String(), topodatapb.TabletType_PRIMARY) - if err != nil { - return nil, nil, destKeyspace, destTabletType, destTarget, err - } - if destKeyspace == "" { - destKeyspace = vw.getActualKeyspace() - } - table, vindex, err := vw.V.FindTableOrVindex(destKeyspace, tab.Name.String(), topodatapb.TabletType_PRIMARY) - if err != nil { - return nil, nil, destKeyspace, destTabletType, destTarget, err - } - return table, vindex, destKeyspace, destTabletType, destTarget, nil + return vw.Vcursor.FindTableOrVindex(tab) } func (vw *VSchemaWrapper) getfirstKeyspace() (ks *vindexes.Keyspace) { @@ -299,12 +300,12 @@ func (vw *VSchemaWrapper) getActualKeyspace() string { return ks.Name } -func (vw *VSchemaWrapper) DefaultKeyspace() (*vindexes.Keyspace, error) { +func (vw *VSchemaWrapper) SelectedKeyspace() (*vindexes.Keyspace, error) { return vw.V.Keyspaces["main"].Keyspace, nil } func (vw *VSchemaWrapper) AnyKeyspace() (*vindexes.Keyspace, error) { - return vw.DefaultKeyspace() + return vw.SelectedKeyspace() } func (vw *VSchemaWrapper) FirstSortedKeyspace() (*vindexes.Keyspace, error) { diff --git a/go/viperutil/debug/debug.go b/go/viperutil/debug/debug.go index 66cbc7f2962..662634a5675 100644 --- a/go/viperutil/debug/debug.go +++ b/go/viperutil/debug/debug.go @@ -25,3 +25,13 @@ import ( func Debug() { registry.Combined().Debug() } + +// WriteConfigAs writes the config into the given filename. +func WriteConfigAs(filename string) error { + return registry.Combined().WriteConfigAs(filename) +} + +// AllSettings gets all the settings in the configuration. +func AllSettings() map[string]any { + return registry.Combined().AllSettings() +} diff --git a/go/vt/mysqlctl/builtinbackupengine.go b/go/vt/mysqlctl/builtinbackupengine.go index f3cbe5364a0..5aa759f6f7a 100644 --- a/go/vt/mysqlctl/builtinbackupengine.go +++ b/go/vt/mysqlctl/builtinbackupengine.go @@ -604,7 +604,15 @@ func (be *BuiltinBackupEngine) backupFiles( wg := sync.WaitGroup{} ctxCancel, cancel := context.WithCancel(ctx) - defer cancel() + defer func() { + // We may still have operations in flight that require a valid context, such as adding files to S3. + // Unless we encountered an error, we should not cancel the context, this is taken care of later + // in the process. If we encountered an error however, we can safely cancel the context as we should + // no longer work on anything and exit fast. + if finalErr != nil { + cancel() + } + }() for i := range fes { wg.Add(1) @@ -1037,7 +1045,15 @@ func (be *BuiltinBackupEngine) restoreFiles(ctx context.Context, params RestoreP wg := sync.WaitGroup{} ctxCancel, cancel := context.WithCancel(ctx) - defer cancel() + defer func() { + // We may still have operations in flight that require a valid context, such as adding files to S3. + // Unless we encountered an error, we should not cancel the context. This is taken care of later + // in the process. If we encountered an error however, we can safely cancel the context as we should + // no longer work on anything and exit fast. + if err != nil { + cancel() + } + }() for i := range fes { wg.Add(1) diff --git a/go/vt/proto/vtadmin/vtadmin.pb.go b/go/vt/proto/vtadmin/vtadmin.pb.go index e85385ec409..8b6a6997c8d 100644 --- a/go/vt/proto/vtadmin/vtadmin.pb.go +++ b/go/vt/proto/vtadmin/vtadmin.pb.go @@ -1203,8 +1203,12 @@ type ApplySchemaRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ClusterId string `protobuf:"bytes,1,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` - Request *vtctldata.ApplySchemaRequest `protobuf:"bytes,2,opt,name=request,proto3" json:"request,omitempty"` + ClusterId string `protobuf:"bytes,1,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` + // Request.Sql will be overriden by this Sql field. + Sql string `protobuf:"bytes,2,opt,name=sql,proto3" json:"sql,omitempty"` + // Request.CallerId will be overriden by this CallerId field. + CallerId string `protobuf:"bytes,3,opt,name=caller_id,json=callerId,proto3" json:"caller_id,omitempty"` + Request *vtctldata.ApplySchemaRequest `protobuf:"bytes,4,opt,name=request,proto3" json:"request,omitempty"` } func (x *ApplySchemaRequest) Reset() { @@ -1244,6 +1248,20 @@ func (x *ApplySchemaRequest) GetClusterId() string { return "" } +func (x *ApplySchemaRequest) GetSql() string { + if x != nil { + return x.Sql + } + return "" +} + +func (x *ApplySchemaRequest) GetCallerId() string { + if x != nil { + return x.CallerId + } + return "" +} + func (x *ApplySchemaRequest) GetRequest() *vtctldata.ApplySchemaRequest { if x != nil { return x.Request @@ -7731,11 +7749,14 @@ var file_vtadmin_proto_rawDesc = []byte{ 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x53, 0x77, 0x69, 0x74, 0x63, 0x68, 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x22, 0x6c, 0x0a, 0x12, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, - 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, - 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x37, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, + 0x22, 0x9b, 0x01, 0x0a, 0x12, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, + 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, + 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x71, 0x6c, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x73, 0x71, 0x6c, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x61, 0x6c, 0x6c, + 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x61, 0x6c, + 0x6c, 0x65, 0x72, 0x49, 0x64, 0x12, 0x37, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x80, 0x01, 0x0a, 0x1c, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4d, diff --git a/go/vt/proto/vtadmin/vtadmin_vtproto.pb.go b/go/vt/proto/vtadmin/vtadmin_vtproto.pb.go index bc0746b7b8a..82cca2cea06 100644 --- a/go/vt/proto/vtadmin/vtadmin_vtproto.pb.go +++ b/go/vt/proto/vtadmin/vtadmin_vtproto.pb.go @@ -454,6 +454,8 @@ func (m *ApplySchemaRequest) CloneVT() *ApplySchemaRequest { } r := new(ApplySchemaRequest) r.ClusterId = m.ClusterId + r.Sql = m.Sql + r.CallerId = m.CallerId r.Request = m.Request.CloneVT() if len(m.unknownFields) > 0 { r.unknownFields = make([]byte, len(m.unknownFields)) @@ -4038,6 +4040,20 @@ func (m *ApplySchemaRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- + dAtA[i] = 0x22 + } + if len(m.CallerId) > 0 { + i -= len(m.CallerId) + copy(dAtA[i:], m.CallerId) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.CallerId))) + i-- + dAtA[i] = 0x1a + } + if len(m.Sql) > 0 { + i -= len(m.Sql) + copy(dAtA[i:], m.Sql) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Sql))) + i-- dAtA[i] = 0x12 } if len(m.ClusterId) > 0 { @@ -10321,6 +10337,14 @@ func (m *ApplySchemaRequest) SizeVT() (n int) { if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } + l = len(m.Sql) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + l = len(m.CallerId) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } if m.Request != nil { l = m.Request.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) @@ -15873,6 +15897,70 @@ func (m *ApplySchemaRequest) UnmarshalVT(dAtA []byte) error { m.ClusterId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Sql", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Sql = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CallerId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CallerId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Request", wireType) } diff --git a/go/vt/proto/vtgate/vtgate.pb.go b/go/vt/proto/vtgate/vtgate.pb.go index f2b2bc47736..b996f919256 100644 --- a/go/vt/proto/vtgate/vtgate.pb.go +++ b/go/vt/proto/vtgate/vtgate.pb.go @@ -1529,6 +1529,8 @@ type Session_ShardSession struct { // reserved connection if a dedicated connection is needed ReservedId int64 `protobuf:"varint,4,opt,name=reserved_id,json=reservedId,proto3" json:"reserved_id,omitempty"` VindexOnly bool `protobuf:"varint,5,opt,name=vindex_only,json=vindexOnly,proto3" json:"vindex_only,omitempty"` + // rows_affected tracks if any query has modified the rows. + RowsAffected bool `protobuf:"varint,6,opt,name=rows_affected,json=rowsAffected,proto3" json:"rows_affected,omitempty"` } func (x *Session_ShardSession) Reset() { @@ -1596,6 +1598,13 @@ func (x *Session_ShardSession) GetVindexOnly() bool { return false } +func (x *Session_ShardSession) GetRowsAffected() bool { + if x != nil { + return x.RowsAffected + } + return false +} + var File_vtgate_proto protoreflect.FileDescriptor var file_vtgate_proto_rawDesc = []byte{ @@ -1604,7 +1613,7 @@ var file_vtgate_proto_rawDesc = []byte{ 0x74, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x0b, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x0e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x0b, 0x76, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x22, 0xfb, 0x0e, 0x0a, 0x07, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x25, + 0x74, 0x6f, 0x22, 0xa0, 0x0f, 0x0a, 0x07, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x25, 0x0a, 0x0e, 0x69, 0x6e, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x69, 0x6e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x43, 0x0a, 0x0e, 0x73, 0x68, 0x61, 0x72, 0x64, 0x5f, 0x73, @@ -1690,7 +1699,7 @@ var file_vtgate_proto_rawDesc = []byte{ 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x2b, 0x0a, 0x11, 0x6d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x1b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x6d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x65, - 0x78, 0x74, 0x1a, 0xd8, 0x01, 0x0a, 0x0c, 0x53, 0x68, 0x61, 0x72, 0x64, 0x53, 0x65, 0x73, 0x73, + 0x78, 0x74, 0x1a, 0xfd, 0x01, 0x0a, 0x0c, 0x53, 0x68, 0x61, 0x72, 0x64, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x25, 0x0a, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x52, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x25, 0x0a, 0x0e, 0x74, 0x72, @@ -1703,195 +1712,197 @@ var file_vtgate_proto_rawDesc = []byte{ 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x49, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x76, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x0a, 0x76, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x6e, 0x6c, 0x79, 0x1a, 0x5c, 0x0a, - 0x19, 0x55, 0x73, 0x65, 0x72, 0x44, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x56, 0x61, 0x72, 0x69, - 0x61, 0x62, 0x6c, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, - 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x29, 0x0a, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x71, 0x75, - 0x65, 0x72, 0x79, 0x2e, 0x42, 0x69, 0x6e, 0x64, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, - 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x42, 0x0a, 0x14, 0x53, - 0x79, 0x73, 0x74, 0x65, 0x6d, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x45, 0x6e, - 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, - 0x3f, 0x0a, 0x11, 0x41, 0x64, 0x76, 0x69, 0x73, 0x6f, 0x72, 0x79, 0x4c, 0x6f, 0x63, 0x6b, 0x45, - 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, - 0x1a, 0x58, 0x0a, 0x15, 0x50, 0x72, 0x65, 0x70, 0x61, 0x72, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, - 0x6d, 0x65, 0x6e, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x29, 0x0a, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x76, 0x74, 0x67, - 0x61, 0x74, 0x65, 0x2e, 0x50, 0x72, 0x65, 0x70, 0x61, 0x72, 0x65, 0x44, 0x61, 0x74, 0x61, 0x52, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, - 0x22, 0x5d, 0x0a, 0x0b, 0x50, 0x72, 0x65, 0x70, 0x61, 0x72, 0x65, 0x44, 0x61, 0x74, 0x61, 0x12, - 0x2b, 0x0a, 0x11, 0x70, 0x72, 0x65, 0x70, 0x61, 0x72, 0x65, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, - 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x70, 0x72, 0x65, 0x70, - 0x61, 0x72, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x21, 0x0a, 0x0c, - 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x05, 0x52, 0x0b, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, - 0xac, 0x01, 0x0a, 0x0e, 0x52, 0x65, 0x61, 0x64, 0x41, 0x66, 0x74, 0x65, 0x72, 0x57, 0x72, 0x69, - 0x74, 0x65, 0x12, 0x31, 0x0a, 0x15, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x61, 0x66, 0x74, 0x65, 0x72, - 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x5f, 0x67, 0x74, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x12, 0x72, 0x65, 0x61, 0x64, 0x41, 0x66, 0x74, 0x65, 0x72, 0x57, 0x72, 0x69, 0x74, - 0x65, 0x47, 0x74, 0x69, 0x64, 0x12, 0x37, 0x0a, 0x18, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x61, 0x66, - 0x74, 0x65, 0x72, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, - 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x15, 0x72, 0x65, 0x61, 0x64, 0x41, 0x66, 0x74, - 0x65, 0x72, 0x57, 0x72, 0x69, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x2e, - 0x0a, 0x13, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x72, 0x61, 0x63, 0x6b, 0x5f, - 0x67, 0x74, 0x69, 0x64, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x73, 0x65, 0x73, - 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x72, 0x61, 0x63, 0x6b, 0x47, 0x74, 0x69, 0x64, 0x73, 0x22, 0xaa, - 0x01, 0x0a, 0x0e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x2c, 0x0a, 0x09, 0x63, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x76, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6c, - 0x6c, 0x65, 0x72, 0x49, 0x44, 0x52, 0x08, 0x63, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x49, 0x64, 0x12, - 0x29, 0x0a, 0x07, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x0f, 0x2e, 0x76, 0x74, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, - 0x6e, 0x52, 0x07, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x27, 0x0a, 0x05, 0x71, 0x75, - 0x65, 0x72, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x71, 0x75, 0x65, 0x72, - 0x79, 0x2e, 0x42, 0x6f, 0x75, 0x6e, 0x64, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x05, 0x71, 0x75, - 0x65, 0x72, 0x79, 0x4a, 0x04, 0x08, 0x04, 0x10, 0x05, 0x4a, 0x04, 0x08, 0x05, 0x10, 0x06, 0x4a, - 0x04, 0x08, 0x06, 0x10, 0x07, 0x4a, 0x04, 0x08, 0x07, 0x10, 0x08, 0x22, 0x8f, 0x01, 0x0a, 0x0f, - 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x25, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, - 0x2e, 0x76, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x50, 0x43, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, - 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x29, 0x0a, 0x07, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, - 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x76, 0x74, 0x67, 0x61, 0x74, 0x65, - 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, - 0x6e, 0x12, 0x2a, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x12, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, - 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0xb3, 0x01, - 0x0a, 0x13, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x42, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2c, 0x0a, 0x09, 0x63, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x5f, - 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x76, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x43, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x49, 0x44, 0x52, 0x08, 0x63, 0x61, 0x6c, 0x6c, 0x65, - 0x72, 0x49, 0x64, 0x12, 0x29, 0x0a, 0x07, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x76, 0x74, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x53, 0x65, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x2b, - 0x0a, 0x07, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x11, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x42, 0x6f, 0x75, 0x6e, 0x64, 0x51, 0x75, 0x65, - 0x72, 0x79, 0x52, 0x07, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x73, 0x4a, 0x04, 0x08, 0x04, 0x10, - 0x05, 0x4a, 0x04, 0x08, 0x05, 0x10, 0x06, 0x4a, 0x04, 0x08, 0x06, 0x10, 0x07, 0x4a, 0x04, 0x08, - 0x07, 0x10, 0x08, 0x22, 0x9a, 0x01, 0x0a, 0x14, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x42, - 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x25, 0x0a, 0x05, - 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x76, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x52, 0x50, 0x43, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x05, 0x65, 0x72, - 0x72, 0x6f, 0x72, 0x12, 0x29, 0x0a, 0x07, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x76, 0x74, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x53, 0x65, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x30, - 0x0a, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x16, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x57, 0x69, - 0x74, 0x68, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, - 0x22, 0xaa, 0x01, 0x0a, 0x14, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x45, 0x78, 0x65, 0x63, 0x75, - 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2c, 0x0a, 0x09, 0x63, 0x61, 0x6c, - 0x6c, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x76, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x49, 0x44, 0x52, 0x08, 0x63, - 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x49, 0x64, 0x12, 0x27, 0x0a, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x42, - 0x6f, 0x75, 0x6e, 0x64, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, - 0x12, 0x29, 0x0a, 0x07, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x0f, 0x2e, 0x76, 0x74, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, - 0x6f, 0x6e, 0x52, 0x07, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4a, 0x04, 0x08, 0x03, 0x10, - 0x04, 0x4a, 0x04, 0x08, 0x04, 0x10, 0x05, 0x4a, 0x04, 0x08, 0x05, 0x10, 0x06, 0x22, 0x6e, 0x0a, - 0x15, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x51, + 0x08, 0x52, 0x0a, 0x76, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x23, 0x0a, + 0x0d, 0x72, 0x6f, 0x77, 0x73, 0x5f, 0x61, 0x66, 0x66, 0x65, 0x63, 0x74, 0x65, 0x64, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x72, 0x6f, 0x77, 0x73, 0x41, 0x66, 0x66, 0x65, 0x63, 0x74, + 0x65, 0x64, 0x1a, 0x5c, 0x0a, 0x19, 0x55, 0x73, 0x65, 0x72, 0x44, 0x65, 0x66, 0x69, 0x6e, 0x65, + 0x64, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, + 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, + 0x79, 0x12, 0x29, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x13, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x42, 0x69, 0x6e, 0x64, 0x56, 0x61, 0x72, + 0x69, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, + 0x1a, 0x42, 0x0a, 0x14, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, + 0x6c, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x3f, 0x0a, 0x11, 0x41, 0x64, 0x76, 0x69, 0x73, 0x6f, 0x72, 0x79, + 0x4c, 0x6f, 0x63, 0x6b, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x58, 0x0a, 0x15, 0x50, 0x72, 0x65, 0x70, 0x61, 0x72, 0x65, + 0x53, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, + 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, + 0x12, 0x29, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x13, 0x2e, 0x76, 0x74, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x50, 0x72, 0x65, 0x70, 0x61, 0x72, 0x65, + 0x44, 0x61, 0x74, 0x61, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x4a, + 0x04, 0x08, 0x03, 0x10, 0x04, 0x22, 0x5d, 0x0a, 0x0b, 0x50, 0x72, 0x65, 0x70, 0x61, 0x72, 0x65, + 0x44, 0x61, 0x74, 0x61, 0x12, 0x2b, 0x0a, 0x11, 0x70, 0x72, 0x65, 0x70, 0x61, 0x72, 0x65, 0x5f, + 0x73, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x10, 0x70, 0x72, 0x65, 0x70, 0x61, 0x72, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, + 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x5f, 0x63, 0x6f, 0x75, 0x6e, + 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x43, + 0x6f, 0x75, 0x6e, 0x74, 0x22, 0xac, 0x01, 0x0a, 0x0e, 0x52, 0x65, 0x61, 0x64, 0x41, 0x66, 0x74, + 0x65, 0x72, 0x57, 0x72, 0x69, 0x74, 0x65, 0x12, 0x31, 0x0a, 0x15, 0x72, 0x65, 0x61, 0x64, 0x5f, + 0x61, 0x66, 0x74, 0x65, 0x72, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x5f, 0x67, 0x74, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x72, 0x65, 0x61, 0x64, 0x41, 0x66, 0x74, 0x65, + 0x72, 0x57, 0x72, 0x69, 0x74, 0x65, 0x47, 0x74, 0x69, 0x64, 0x12, 0x37, 0x0a, 0x18, 0x72, 0x65, + 0x61, 0x64, 0x5f, 0x61, 0x66, 0x74, 0x65, 0x72, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x5f, 0x74, + 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x15, 0x72, 0x65, + 0x61, 0x64, 0x41, 0x66, 0x74, 0x65, 0x72, 0x57, 0x72, 0x69, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, + 0x6f, 0x75, 0x74, 0x12, 0x2e, 0x0a, 0x13, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x74, + 0x72, 0x61, 0x63, 0x6b, 0x5f, 0x67, 0x74, 0x69, 0x64, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x11, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x72, 0x61, 0x63, 0x6b, 0x47, 0x74, + 0x69, 0x64, 0x73, 0x22, 0xaa, 0x01, 0x0a, 0x0e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2c, 0x0a, 0x09, 0x63, 0x61, 0x6c, 0x6c, 0x65, 0x72, + 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x76, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x43, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x49, 0x44, 0x52, 0x08, 0x63, 0x61, 0x6c, 0x6c, + 0x65, 0x72, 0x49, 0x64, 0x12, 0x29, 0x0a, 0x07, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x76, 0x74, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x53, + 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, + 0x27, 0x0a, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, + 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x42, 0x6f, 0x75, 0x6e, 0x64, 0x51, 0x75, 0x65, 0x72, + 0x79, 0x52, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x4a, 0x04, 0x08, 0x04, 0x10, 0x05, 0x4a, 0x04, + 0x08, 0x05, 0x10, 0x06, 0x4a, 0x04, 0x08, 0x06, 0x10, 0x07, 0x4a, 0x04, 0x08, 0x07, 0x10, 0x08, + 0x22, 0x8f, 0x01, 0x0a, 0x0f, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x25, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x76, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x50, 0x43, 0x45, + 0x72, 0x72, 0x6f, 0x72, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x29, 0x0a, 0x07, 0x73, + 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x76, + 0x74, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x73, + 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x2a, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, - 0x6c, 0x74, 0x12, 0x29, 0x0a, 0x07, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x76, 0x74, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x53, 0x65, 0x73, - 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x5d, 0x0a, - 0x19, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2c, 0x0a, 0x09, 0x63, 0x61, + 0x6c, 0x74, 0x22, 0xb3, 0x01, 0x0a, 0x13, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x42, 0x61, + 0x74, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2c, 0x0a, 0x09, 0x63, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x76, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x49, 0x44, 0x52, 0x08, - 0x63, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x74, 0x69, 0x64, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x74, 0x69, 0x64, 0x22, 0x1c, 0x0a, 0x1a, - 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xef, 0x02, 0x0a, 0x0c, 0x56, - 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x6d, - 0x69, 0x6e, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x5f, 0x73, 0x6b, 0x65, 0x77, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x0c, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x53, 0x6b, 0x65, 0x77, - 0x12, 0x2d, 0x0a, 0x12, 0x68, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x5f, 0x69, 0x6e, - 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x11, 0x68, 0x65, - 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, - 0x26, 0x0a, 0x0f, 0x73, 0x74, 0x6f, 0x70, 0x5f, 0x6f, 0x6e, 0x5f, 0x72, 0x65, 0x73, 0x68, 0x61, - 0x72, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x73, 0x74, 0x6f, 0x70, 0x4f, 0x6e, - 0x52, 0x65, 0x73, 0x68, 0x61, 0x72, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x12, 0x27, 0x0a, - 0x0f, 0x63, 0x65, 0x6c, 0x6c, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x63, 0x65, 0x6c, 0x6c, 0x50, 0x72, 0x65, 0x66, - 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, - 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x74, 0x61, - 0x62, 0x6c, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x3c, 0x0a, 0x1a, 0x73, 0x74, 0x72, - 0x65, 0x61, 0x6d, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x68, 0x65, 0x61, - 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x18, 0x73, - 0x74, 0x72, 0x65, 0x61, 0x6d, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x48, 0x65, 0x61, - 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x73, 0x12, 0x43, 0x0a, 0x1e, 0x69, 0x6e, 0x63, 0x6c, 0x75, - 0x64, 0x65, 0x5f, 0x72, 0x65, 0x73, 0x68, 0x61, 0x72, 0x64, 0x5f, 0x6a, 0x6f, 0x75, 0x72, 0x6e, - 0x61, 0x6c, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x1b, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x52, 0x65, 0x73, 0x68, 0x61, 0x72, 0x64, 0x4a, - 0x6f, 0x75, 0x72, 0x6e, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x22, 0xf6, 0x01, 0x0a, - 0x0e, 0x56, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x63, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x49, 0x64, 0x12, 0x29, 0x0a, 0x07, 0x73, 0x65, 0x73, 0x73, + 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x76, 0x74, 0x67, 0x61, + 0x74, 0x65, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x73, 0x65, 0x73, 0x73, + 0x69, 0x6f, 0x6e, 0x12, 0x2b, 0x0a, 0x07, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x73, 0x18, 0x03, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x42, 0x6f, 0x75, + 0x6e, 0x64, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x07, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x73, + 0x4a, 0x04, 0x08, 0x04, 0x10, 0x05, 0x4a, 0x04, 0x08, 0x05, 0x10, 0x06, 0x4a, 0x04, 0x08, 0x06, + 0x10, 0x07, 0x4a, 0x04, 0x08, 0x07, 0x10, 0x08, 0x22, 0x9a, 0x01, 0x0a, 0x14, 0x45, 0x78, 0x65, + 0x63, 0x75, 0x74, 0x65, 0x42, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x25, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x0f, 0x2e, 0x76, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x50, 0x43, 0x45, 0x72, 0x72, 0x6f, + 0x72, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x29, 0x0a, 0x07, 0x73, 0x65, 0x73, 0x73, + 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x76, 0x74, 0x67, 0x61, + 0x74, 0x65, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x73, 0x65, 0x73, 0x73, + 0x69, 0x6f, 0x6e, 0x12, 0x30, 0x0a, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x03, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x52, 0x65, 0x73, + 0x75, 0x6c, 0x74, 0x57, 0x69, 0x74, 0x68, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x07, 0x72, 0x65, + 0x73, 0x75, 0x6c, 0x74, 0x73, 0x22, 0xaa, 0x01, 0x0a, 0x14, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, + 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2c, + 0x0a, 0x09, 0x63, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x0f, 0x2e, 0x76, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6c, 0x6c, 0x65, 0x72, + 0x49, 0x44, 0x52, 0x08, 0x63, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x49, 0x64, 0x12, 0x27, 0x0a, 0x05, + 0x71, 0x75, 0x65, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x71, 0x75, + 0x65, 0x72, 0x79, 0x2e, 0x42, 0x6f, 0x75, 0x6e, 0x64, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x05, + 0x71, 0x75, 0x65, 0x72, 0x79, 0x12, 0x29, 0x0a, 0x07, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x76, 0x74, 0x67, 0x61, 0x74, 0x65, 0x2e, + 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, + 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x4a, 0x04, 0x08, 0x04, 0x10, 0x05, 0x4a, 0x04, 0x08, 0x05, + 0x10, 0x06, 0x22, 0x6e, 0x0a, 0x15, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x45, 0x78, 0x65, 0x63, + 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, 0x06, 0x72, + 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x71, 0x75, + 0x65, 0x72, 0x79, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, + 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x29, 0x0a, 0x07, 0x73, 0x65, 0x73, 0x73, 0x69, + 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x76, 0x74, 0x67, 0x61, 0x74, + 0x65, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x73, 0x65, 0x73, 0x73, 0x69, + 0x6f, 0x6e, 0x22, 0x5d, 0x0a, 0x19, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x54, 0x72, 0x61, + 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2c, 0x0a, 0x09, 0x63, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x76, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6c, 0x6c, 0x65, - 0x72, 0x49, 0x44, 0x52, 0x08, 0x63, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x49, 0x64, 0x12, 0x35, 0x0a, - 0x0b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0e, 0x32, 0x14, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, - 0x62, 0x6c, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0a, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, - 0x54, 0x79, 0x70, 0x65, 0x12, 0x27, 0x0a, 0x05, 0x76, 0x67, 0x74, 0x69, 0x64, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x62, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x64, 0x61, 0x74, 0x61, - 0x2e, 0x56, 0x47, 0x74, 0x69, 0x64, 0x52, 0x05, 0x76, 0x67, 0x74, 0x69, 0x64, 0x12, 0x2a, 0x0a, - 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, - 0x62, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x46, 0x69, 0x6c, 0x74, 0x65, - 0x72, 0x52, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x2a, 0x0a, 0x05, 0x66, 0x6c, 0x61, - 0x67, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x76, 0x74, 0x67, 0x61, 0x74, - 0x65, 0x2e, 0x56, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x52, 0x05, - 0x66, 0x6c, 0x61, 0x67, 0x73, 0x22, 0x3d, 0x0a, 0x0f, 0x56, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, 0x06, 0x65, 0x76, 0x65, 0x6e, - 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x62, 0x69, 0x6e, 0x6c, 0x6f, - 0x67, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x56, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x06, 0x65, 0x76, - 0x65, 0x6e, 0x74, 0x73, 0x22, 0x92, 0x01, 0x0a, 0x0e, 0x50, 0x72, 0x65, 0x70, 0x61, 0x72, 0x65, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2c, 0x0a, 0x09, 0x63, 0x61, 0x6c, 0x6c, 0x65, - 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x76, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x49, 0x44, 0x52, 0x08, 0x63, 0x61, 0x6c, - 0x6c, 0x65, 0x72, 0x49, 0x64, 0x12, 0x29, 0x0a, 0x07, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x76, 0x74, 0x67, 0x61, 0x74, 0x65, 0x2e, - 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, - 0x12, 0x27, 0x0a, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x11, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x42, 0x6f, 0x75, 0x6e, 0x64, 0x51, 0x75, 0x65, - 0x72, 0x79, 0x52, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x22, 0x89, 0x01, 0x0a, 0x0f, 0x50, 0x72, - 0x65, 0x70, 0x61, 0x72, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x25, 0x0a, - 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x76, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x50, 0x43, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x05, 0x65, - 0x72, 0x72, 0x6f, 0x72, 0x12, 0x29, 0x0a, 0x07, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x76, 0x74, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x53, - 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, - 0x24, 0x0a, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x0c, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x06, 0x66, - 0x69, 0x65, 0x6c, 0x64, 0x73, 0x22, 0x6e, 0x0a, 0x13, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x53, 0x65, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2c, 0x0a, 0x09, + 0x72, 0x49, 0x44, 0x52, 0x08, 0x63, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x49, 0x64, 0x12, 0x12, 0x0a, + 0x04, 0x64, 0x74, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x74, 0x69, + 0x64, 0x22, 0x1c, 0x0a, 0x1a, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x54, 0x72, 0x61, 0x6e, + 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0xef, 0x02, 0x0a, 0x0c, 0x56, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x46, 0x6c, 0x61, 0x67, 0x73, + 0x12, 0x23, 0x0a, 0x0d, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x5f, 0x73, 0x6b, 0x65, + 0x77, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x69, 0x7a, + 0x65, 0x53, 0x6b, 0x65, 0x77, 0x12, 0x2d, 0x0a, 0x12, 0x68, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, + 0x61, 0x74, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x11, 0x68, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x49, 0x6e, 0x74, 0x65, + 0x72, 0x76, 0x61, 0x6c, 0x12, 0x26, 0x0a, 0x0f, 0x73, 0x74, 0x6f, 0x70, 0x5f, 0x6f, 0x6e, 0x5f, + 0x72, 0x65, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x73, + 0x74, 0x6f, 0x70, 0x4f, 0x6e, 0x52, 0x65, 0x73, 0x68, 0x61, 0x72, 0x64, 0x12, 0x14, 0x0a, 0x05, + 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x63, 0x65, 0x6c, + 0x6c, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x63, 0x65, 0x6c, 0x6c, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x65, + 0x72, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x63, 0x65, 0x6c, + 0x6c, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x74, + 0x61, 0x62, 0x6c, 0x65, 0x74, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x3c, + 0x0a, 0x1a, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x5f, 0x68, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x73, 0x18, 0x07, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x18, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x73, 0x12, 0x43, 0x0a, 0x1e, + 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x72, 0x65, 0x73, 0x68, 0x61, 0x72, 0x64, 0x5f, + 0x6a, 0x6f, 0x75, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x08, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x1b, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x52, 0x65, 0x73, + 0x68, 0x61, 0x72, 0x64, 0x4a, 0x6f, 0x75, 0x72, 0x6e, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, + 0x73, 0x22, 0xf6, 0x01, 0x0a, 0x0e, 0x56, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x2c, 0x0a, 0x09, 0x63, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x5f, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x76, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x43, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x49, 0x44, 0x52, 0x08, 0x63, 0x61, 0x6c, 0x6c, 0x65, 0x72, + 0x49, 0x64, 0x12, 0x35, 0x0a, 0x0b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x5f, 0x74, 0x79, 0x70, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x14, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, + 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0a, 0x74, + 0x61, 0x62, 0x6c, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x27, 0x0a, 0x05, 0x76, 0x67, 0x74, + 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x62, 0x69, 0x6e, 0x6c, 0x6f, + 0x67, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x56, 0x47, 0x74, 0x69, 0x64, 0x52, 0x05, 0x76, 0x67, 0x74, + 0x69, 0x64, 0x12, 0x2a, 0x0a, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x62, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x64, 0x61, 0x74, 0x61, 0x2e, + 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x2a, + 0x0a, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, + 0x76, 0x74, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x56, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x46, 0x6c, + 0x61, 0x67, 0x73, 0x52, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x22, 0x3d, 0x0a, 0x0f, 0x56, 0x53, + 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, + 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, + 0x62, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x56, 0x45, 0x76, 0x65, 0x6e, + 0x74, 0x52, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x92, 0x01, 0x0a, 0x0e, 0x50, 0x72, + 0x65, 0x70, 0x61, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2c, 0x0a, 0x09, 0x63, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x76, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x49, 0x44, 0x52, 0x08, 0x63, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x49, 0x64, 0x12, 0x29, 0x0a, 0x07, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x76, 0x74, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x73, 0x65, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x3d, 0x0a, 0x14, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x53, 0x65, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x25, 0x0a, - 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x76, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x50, 0x43, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x05, 0x65, - 0x72, 0x72, 0x6f, 0x72, 0x2a, 0x44, 0x0a, 0x0f, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x0f, 0x0a, 0x0b, 0x55, 0x4e, 0x53, 0x50, 0x45, - 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x49, 0x4e, 0x47, - 0x4c, 0x45, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x4d, 0x55, 0x4c, 0x54, 0x49, 0x10, 0x02, 0x12, - 0x09, 0x0a, 0x05, 0x54, 0x57, 0x4f, 0x50, 0x43, 0x10, 0x03, 0x2a, 0x3c, 0x0a, 0x0b, 0x43, 0x6f, - 0x6d, 0x6d, 0x69, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x0a, 0x0a, 0x06, 0x4e, 0x4f, 0x52, - 0x4d, 0x41, 0x4c, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x50, 0x52, 0x45, 0x10, 0x01, 0x12, 0x08, - 0x0a, 0x04, 0x50, 0x4f, 0x53, 0x54, 0x10, 0x02, 0x12, 0x0e, 0x0a, 0x0a, 0x41, 0x55, 0x54, 0x4f, - 0x43, 0x4f, 0x4d, 0x4d, 0x49, 0x54, 0x10, 0x03, 0x42, 0x36, 0x0a, 0x0f, 0x69, 0x6f, 0x2e, 0x76, - 0x69, 0x74, 0x65, 0x73, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x5a, 0x23, 0x76, 0x69, 0x74, - 0x65, 0x73, 0x73, 0x2e, 0x69, 0x6f, 0x2f, 0x76, 0x69, 0x74, 0x65, 0x73, 0x73, 0x2f, 0x67, 0x6f, - 0x2f, 0x76, 0x74, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x76, 0x74, 0x67, 0x61, 0x74, 0x65, - 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x27, 0x0a, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x42, 0x6f, 0x75, + 0x6e, 0x64, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x22, 0x89, + 0x01, 0x0a, 0x0f, 0x50, 0x72, 0x65, 0x70, 0x61, 0x72, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x25, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x0f, 0x2e, 0x76, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x50, 0x43, 0x45, 0x72, 0x72, + 0x6f, 0x72, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x29, 0x0a, 0x07, 0x73, 0x65, 0x73, + 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x76, 0x74, 0x67, + 0x61, 0x74, 0x65, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x73, 0x65, 0x73, + 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x24, 0x0a, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, 0x03, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x46, 0x69, 0x65, + 0x6c, 0x64, 0x52, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x22, 0x6e, 0x0a, 0x13, 0x43, 0x6c, + 0x6f, 0x73, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x2c, 0x0a, 0x09, 0x63, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x76, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6c, + 0x6c, 0x65, 0x72, 0x49, 0x44, 0x52, 0x08, 0x63, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x49, 0x64, 0x12, + 0x29, 0x0a, 0x07, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x0f, 0x2e, 0x76, 0x74, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x52, 0x07, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x3d, 0x0a, 0x14, 0x43, 0x6c, + 0x6f, 0x73, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x25, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x0f, 0x2e, 0x76, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x50, 0x43, 0x45, 0x72, 0x72, + 0x6f, 0x72, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x2a, 0x44, 0x0a, 0x0f, 0x54, 0x72, 0x61, + 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x0f, 0x0a, 0x0b, + 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0a, 0x0a, + 0x06, 0x53, 0x49, 0x4e, 0x47, 0x4c, 0x45, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x4d, 0x55, 0x4c, + 0x54, 0x49, 0x10, 0x02, 0x12, 0x09, 0x0a, 0x05, 0x54, 0x57, 0x4f, 0x50, 0x43, 0x10, 0x03, 0x2a, + 0x3c, 0x0a, 0x0b, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x0a, + 0x0a, 0x06, 0x4e, 0x4f, 0x52, 0x4d, 0x41, 0x4c, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x50, 0x52, + 0x45, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x50, 0x4f, 0x53, 0x54, 0x10, 0x02, 0x12, 0x0e, 0x0a, + 0x0a, 0x41, 0x55, 0x54, 0x4f, 0x43, 0x4f, 0x4d, 0x4d, 0x49, 0x54, 0x10, 0x03, 0x42, 0x36, 0x0a, + 0x0f, 0x69, 0x6f, 0x2e, 0x76, 0x69, 0x74, 0x65, 0x73, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x5a, 0x23, 0x76, 0x69, 0x74, 0x65, 0x73, 0x73, 0x2e, 0x69, 0x6f, 0x2f, 0x76, 0x69, 0x74, 0x65, + 0x73, 0x73, 0x2f, 0x67, 0x6f, 0x2f, 0x76, 0x74, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x76, + 0x74, 0x67, 0x61, 0x74, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/go/vt/proto/vtgate/vtgate_vtproto.pb.go b/go/vt/proto/vtgate/vtgate_vtproto.pb.go index 5d9393b881c..5df6dbbffef 100644 --- a/go/vt/proto/vtgate/vtgate_vtproto.pb.go +++ b/go/vt/proto/vtgate/vtgate_vtproto.pb.go @@ -35,6 +35,7 @@ func (m *Session_ShardSession) CloneVT() *Session_ShardSession { r.TabletAlias = m.TabletAlias.CloneVT() r.ReservedId = m.ReservedId r.VindexOnly = m.VindexOnly + r.RowsAffected = m.RowsAffected if len(m.unknownFields) > 0 { r.unknownFields = make([]byte, len(m.unknownFields)) copy(r.unknownFields, m.unknownFields) @@ -513,6 +514,16 @@ func (m *Session_ShardSession) MarshalToSizedBufferVT(dAtA []byte) (int, error) i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } + if m.RowsAffected { + i-- + if m.RowsAffected { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x30 + } if m.VindexOnly { i-- if m.VindexOnly { @@ -1903,6 +1914,9 @@ func (m *Session_ShardSession) SizeVT() (n int) { if m.VindexOnly { n += 2 } + if m.RowsAffected { + n += 2 + } n += len(m.unknownFields) return n } @@ -2563,6 +2577,26 @@ func (m *Session_ShardSession) UnmarshalVT(dAtA []byte) error { } } m.VindexOnly = bool(v != 0) + case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field RowsAffected", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.RowsAffected = bool(v != 0) default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) diff --git a/go/vt/schemamanager/local_controller.go b/go/vt/schemamanager/local_controller.go index b95bb1398bf..92b7afa83c0 100644 --- a/go/vt/schemamanager/local_controller.go +++ b/go/vt/schemamanager/local_controller.go @@ -208,8 +208,10 @@ func (controller *LocalController) writeToLogDir(ctx context.Context, result *Ex rowsReturned := uint64(0) rowsAffected := uint64(0) for _, queryResult := range result.SuccessShards { - rowsReturned += uint64(len(queryResult.Result.Rows)) - rowsAffected += queryResult.Result.RowsAffected + for _, result := range queryResult.Results { + rowsReturned += uint64(len(result.Rows)) + rowsAffected += result.RowsAffected + } } logFile.WriteString(fmt.Sprintf("-- Rows returned: %d\n", rowsReturned)) logFile.WriteString(fmt.Sprintf("-- Rows affected: %d\n", rowsAffected)) diff --git a/go/vt/schemamanager/local_controller_test.go b/go/vt/schemamanager/local_controller_test.go index 1784a76e133..74d66033c63 100644 --- a/go/vt/schemamanager/local_controller_test.go +++ b/go/vt/schemamanager/local_controller_test.go @@ -138,8 +138,8 @@ func TestLocalControllerSchemaChange(t *testing.T) { result := &ExecuteResult{ Sqls: []string{"create table test_table (id int)"}, SuccessShards: []ShardResult{{ - Shard: "0", - Result: &querypb.QueryResult{}, + Shard: "0", + Results: []*querypb.QueryResult{{}}, }}, } logPath := path.Join(controller.logDir, controller.sqlFilename) diff --git a/go/vt/schemamanager/schemamanager.go b/go/vt/schemamanager/schemamanager.go index 1ccc52d6a6d..8a695a1164e 100644 --- a/go/vt/schemamanager/schemamanager.go +++ b/go/vt/schemamanager/schemamanager.go @@ -85,8 +85,8 @@ type ShardWithError struct { // ShardResult contains sql execute information on a particular shard type ShardResult struct { - Shard string - Result *querypb.QueryResult + Shard string + Results []*querypb.QueryResult // Position is a replication position that is guaranteed to be after the // schema change was applied. It can be used to wait for replicas to receive // the schema change via replication. diff --git a/go/vt/schemamanager/schemamanager_test.go b/go/vt/schemamanager/schemamanager_test.go index 129600d0527..76b638960fe 100644 --- a/go/vt/schemamanager/schemamanager_test.go +++ b/go/vt/schemamanager/schemamanager_test.go @@ -293,6 +293,13 @@ func (client *fakeTabletManagerClient) ExecuteFetchAsDba(ctx context.Context, ta return client.TabletManagerClient.ExecuteFetchAsDba(ctx, tablet, usePool, req) } +func (client *fakeTabletManagerClient) ExecuteMultiFetchAsDba(ctx context.Context, tablet *topodatapb.Tablet, usePool bool, req *tabletmanagerdatapb.ExecuteMultiFetchAsDbaRequest) ([]*querypb.QueryResult, error) { + if client.EnableExecuteFetchAsDbaError { + return nil, fmt.Errorf("ExecuteMultiFetchAsDba occur an unknown error") + } + return client.TabletManagerClient.ExecuteMultiFetchAsDba(ctx, tablet, usePool, req) +} + // newFakeTopo returns a topo with: // - a keyspace named 'test_keyspace'. // - 3 shards named '1', '2', '3'. diff --git a/go/vt/schemamanager/tablet_executor.go b/go/vt/schemamanager/tablet_executor.go index c6befa4e743..60e86bf7faf 100644 --- a/go/vt/schemamanager/tablet_executor.go +++ b/go/vt/schemamanager/tablet_executor.go @@ -570,13 +570,15 @@ func (exec *TabletExecutor) executeOneTablet( errChan chan ShardWithError, successChan chan ShardResult) { - var result *querypb.QueryResult + var results []*querypb.QueryResult var err error if viaQueryService { - result, err = exec.tmc.ExecuteQuery(ctx, tablet, &tabletmanagerdatapb.ExecuteQueryRequest{ + result, reserr := exec.tmc.ExecuteQuery(ctx, tablet, &tabletmanagerdatapb.ExecuteQueryRequest{ Query: []byte(sql), MaxRows: 10, }) + results = []*querypb.QueryResult{result} + err = reserr } else { if exec.ddlStrategySetting != nil && exec.ddlStrategySetting.IsAllowZeroInDateFlag() { // --allow-zero-in-date Applies to DDLs @@ -586,14 +588,14 @@ func (exec *TabletExecutor) executeOneTablet( return } } - request := &tabletmanagerdatapb.ExecuteFetchAsDbaRequest{ - Query: []byte(sql), + request := &tabletmanagerdatapb.ExecuteMultiFetchAsDbaRequest{ + Sql: []byte(sql), MaxRows: 10, } if exec.ddlStrategySetting != nil && exec.ddlStrategySetting.IsAllowForeignKeysFlag() { request.DisableForeignKeyChecks = true } - result, err = exec.tmc.ExecuteFetchAsDba(ctx, tablet, false, request) + results, err = exec.tmc.ExecuteMultiFetchAsDba(ctx, tablet, false, request) } if err != nil { @@ -612,7 +614,7 @@ func (exec *TabletExecutor) executeOneTablet( } successChan <- ShardResult{ Shard: tablet.Shard, - Result: result, + Results: results, Position: pos, } } diff --git a/go/vt/servenv/servenv.go b/go/vt/servenv/servenv.go index 4aa9818eb7d..22bf3523dfc 100644 --- a/go/vt/servenv/servenv.go +++ b/go/vt/servenv/servenv.go @@ -370,6 +370,14 @@ func moveFlags(name string, fs *pflag.FlagSet) { // functions. func CobraPreRunE(cmd *cobra.Command, args []string) error { _flag.TrickGlog() + // Register logging on config file change. + ch := make(chan struct{}) + viperutil.NotifyConfigReload(ch) + go func() { + for range ch { + log.Infof("Change in configuration - %v", viperdebug.AllSettings()) + } + }() watchCancel, err := viperutil.LoadConfig() if err != nil { @@ -377,6 +385,10 @@ func CobraPreRunE(cmd *cobra.Command, args []string) error { } OnTerm(watchCancel) + // Register a function to be called on termination that closes the channel. + // This is done after the watchCancel has registered to ensure that we don't end up + // sending on a closed channel. + OnTerm(func() { close(ch) }) HTTPHandleFunc("/debug/config", viperdebug.HandlerFunc) logutil.PurgeLogs() diff --git a/go/vt/sidecardb/schema/onlineddl/schema_migrations.sql b/go/vt/sidecardb/schema/onlineddl/schema_migrations.sql index 91c0323e54b..3e2a72d6ae5 100644 --- a/go/vt/sidecardb/schema/onlineddl/schema_migrations.sql +++ b/go/vt/sidecardb/schema/onlineddl/schema_migrations.sql @@ -45,6 +45,7 @@ CREATE TABLE IF NOT EXISTS schema_migrations `message_timestamp` timestamp(6) NULL DEFAULT NULL, `eta_seconds` bigint NOT NULL DEFAULT '-1', `rows_copied` bigint unsigned NOT NULL DEFAULT '0', + `vreplication_lag_seconds` bigint unsigned NOT NULL DEFAULT '0', `table_rows` bigint NOT NULL DEFAULT '0', `added_unique_keys` int unsigned NOT NULL DEFAULT '0', `removed_unique_keys` int unsigned NOT NULL DEFAULT '0', diff --git a/go/vt/vtadmin/api.go b/go/vt/vtadmin/api.go index cef8816504a..4f91459d9ed 100644 --- a/go/vt/vtadmin/api.go +++ b/go/vt/vtadmin/api.go @@ -59,6 +59,7 @@ import ( "vitess.io/vitess/go/vt/vtadmin/rbac" "vitess.io/vitess/go/vt/vtadmin/sort" "vitess.io/vitess/go/vt/vtadmin/vtadminproto" + "vitess.io/vitess/go/vt/vtctl/grpcvtctldserver" "vitess.io/vitess/go/vt/vtctl/workflow" "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vterrors" @@ -488,6 +489,31 @@ func (api *API) ApplySchema(ctx context.Context, req *vtadminpb.ApplySchemaReque return nil, err } + // Parser with default options. New() itself initializes with default MySQL version. + parser, err := sqlparser.New(sqlparser.Options{ + TruncateUILen: 512, + TruncateErrLen: 0, + }) + if err != nil { + return nil, err + } + + // Split the sql statement received from request. + sqlParts, err := parser.SplitStatementToPieces(req.Sql) + if err != nil { + return nil, err + } + + req.Request.Sql = sqlParts + + // Set the callerID if not empty. + if req.CallerId != "" { + req.Request.CallerId = &vtrpcpb.CallerID{Principal: req.CallerId} + } + + // Set the default wait replicas timeout. + req.Request.WaitReplicasTimeout = protoutil.DurationToProto(grpcvtctldserver.DefaultWaitReplicasTimeout) + return c.ApplySchema(ctx, req.Request) } diff --git a/go/vt/vtadmin/http/schema_migrations.go b/go/vt/vtadmin/http/schema_migrations.go index e0207989648..3da6026fe9f 100644 --- a/go/vt/vtadmin/http/schema_migrations.go +++ b/go/vt/vtadmin/http/schema_migrations.go @@ -34,19 +34,26 @@ func ApplySchema(ctx context.Context, r Request, api *API) *JSONResponse { decoder := json.NewDecoder(r.Body) defer r.Body.Close() - var req vtctldatapb.ApplySchemaRequest - if err := decoder.Decode(&req); err != nil { + var body struct { + Sql string `json:"sql"` + CallerId string `json:"caller_id"` + Request vtctldatapb.ApplySchemaRequest `json:"request"` + } + + if err := decoder.Decode(&body); err != nil { return NewJSONResponse(nil, &errors.BadRequest{ Err: err, }) } vars := mux.Vars(r.Request) - req.Keyspace = vars["keyspace"] + body.Request.Keyspace = vars["keyspace"] resp, err := api.server.ApplySchema(ctx, &vtadminpb.ApplySchemaRequest{ ClusterId: vars["cluster_id"], - Request: &req, + Sql: body.Sql, + CallerId: body.CallerId, + Request: &body.Request, }) return NewJSONResponse(resp, err) diff --git a/go/vt/vtctl/grpcvtctldserver/server.go b/go/vt/vtctl/grpcvtctldserver/server.go index 3c1a2dd537e..c3dc22d21b4 100644 --- a/go/vt/vtctl/grpcvtctldserver/server.go +++ b/go/vt/vtctl/grpcvtctldserver/server.go @@ -303,7 +303,9 @@ func (s *VtctldServer) ApplySchema(ctx context.Context, req *vtctldatapb.ApplySc } for _, shard := range execResult.SuccessShards { - resp.RowsAffectedByShard[shard.Shard] = shard.Result.RowsAffected + for _, result := range shard.Results { + resp.RowsAffectedByShard[shard.Shard] += result.RowsAffected + } } return resp, err diff --git a/go/vt/vtexplain/vtexplain_vtgate.go b/go/vt/vtexplain/vtexplain_vtgate.go index d511e2d2ea0..f9ae8be3820 100644 --- a/go/vt/vtexplain/vtexplain_vtgate.go +++ b/go/vt/vtexplain/vtexplain_vtgate.go @@ -38,6 +38,7 @@ import ( "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vtgate" "vitess.io/vitess/go/vt/vtgate/engine" + econtext "vitess.io/vitess/go/vt/vtgate/executorcontext" "vitess.io/vitess/go/vt/vtgate/logstats" "vitess.io/vitess/go/vt/vtgate/vindexes" "vitess.io/vitess/go/vt/vttablet/queryservice" @@ -235,7 +236,7 @@ func (vte *VTExplain) vtgateExecute(sql string) ([]*engine.Plan, map[string]*Tab // This will ensure that the commit/rollback order is predictable. vte.sortShardSession() - _, err := vte.vtgateExecutor.Execute(context.Background(), nil, "VtexplainExecute", vtgate.NewSafeSession(vte.vtgateSession), sql, nil) + _, err := vte.vtgateExecutor.Execute(context.Background(), nil, "VtexplainExecute", econtext.NewSafeSession(vte.vtgateSession), sql, nil) if err != nil { for _, tc := range vte.explainTopo.TabletConns { tc.tabletQueries = nil diff --git a/go/vt/vtgate/autocommit_test.go b/go/vt/vtgate/autocommit_test.go index 1ba99c01ef2..2e65cefbabe 100644 --- a/go/vt/vtgate/autocommit_test.go +++ b/go/vt/vtgate/autocommit_test.go @@ -23,10 +23,10 @@ import ( "github.com/stretchr/testify/require" "vitess.io/vitess/go/sqltypes" - querypb "vitess.io/vitess/go/vt/proto/query" vtgatepb "vitess.io/vitess/go/vt/proto/vtgate" vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" + econtext "vitess.io/vitess/go/vt/vtgate/executorcontext" ) // This file contains tests for all the autocommit code paths @@ -382,7 +382,7 @@ func TestAutocommitTransactionStarted(t *testing.T) { // single shard query - no savepoint needed sql := "update `user` set a = 2 where id = 1" - _, err := executor.Execute(context.Background(), nil, "TestExecute", NewSafeSession(session), sql, map[string]*querypb.BindVariable{}) + _, err := executor.Execute(context.Background(), nil, "TestExecute", econtext.NewSafeSession(session), sql, map[string]*querypb.BindVariable{}) require.NoError(t, err) require.Len(t, sbc1.Queries, 1) require.Equal(t, sql, sbc1.Queries[0].Sql) @@ -394,7 +394,7 @@ func TestAutocommitTransactionStarted(t *testing.T) { // multi shard query - savepoint needed sql = "update `user` set a = 2 where id in (1, 4)" expectedSql := "update `user` set a = 2 where id in ::__vals" - _, err = executor.Execute(context.Background(), nil, "TestExecute", NewSafeSession(session), sql, map[string]*querypb.BindVariable{}) + _, err = executor.Execute(context.Background(), nil, "TestExecute", econtext.NewSafeSession(session), sql, map[string]*querypb.BindVariable{}) require.NoError(t, err) require.Len(t, sbc1.Queries, 2) require.Contains(t, sbc1.Queries[0].Sql, "savepoint") @@ -413,7 +413,7 @@ func TestAutocommitDirectTarget(t *testing.T) { } sql := "insert into `simple`(val) values ('val')" - _, err := executor.Execute(context.Background(), nil, "TestExecute", NewSafeSession(session), sql, map[string]*querypb.BindVariable{}) + _, err := executor.Execute(context.Background(), nil, "TestExecute", econtext.NewSafeSession(session), sql, map[string]*querypb.BindVariable{}) require.NoError(t, err) assertQueries(t, sbclookup, []*querypb.BoundQuery{{ @@ -434,7 +434,7 @@ func TestAutocommitDirectRangeTarget(t *testing.T) { } sql := "delete from sharded_user_msgs limit 1000" - _, err := executor.Execute(context.Background(), nil, "TestExecute", NewSafeSession(session), sql, map[string]*querypb.BindVariable{}) + _, err := executor.Execute(context.Background(), nil, "TestExecute", econtext.NewSafeSession(session), sql, map[string]*querypb.BindVariable{}) require.NoError(t, err) assertQueries(t, sbc1, []*querypb.BoundQuery{{ @@ -451,5 +451,5 @@ func autocommitExec(executor *Executor, sql string) (*sqltypes.Result, error) { TransactionMode: vtgatepb.TransactionMode_MULTI, } - return executor.Execute(context.Background(), nil, "TestExecute", NewSafeSession(session), sql, map[string]*querypb.BindVariable{}) + return executor.Execute(context.Background(), nil, "TestExecute", econtext.NewSafeSession(session), sql, map[string]*querypb.BindVariable{}) } diff --git a/go/vt/vtgate/debugenv.go b/go/vt/vtgate/debugenv.go index 4fa989c69a3..7213353432d 100644 --- a/go/vt/vtgate/debugenv.go +++ b/go/vt/vtgate/debugenv.go @@ -22,9 +22,10 @@ import ( "html" "net/http" "strconv" - "text/template" "time" + "github.com/google/safehtml/template" + "vitess.io/vitess/go/acl" "vitess.io/vitess/go/vt/discovery" "vitess.io/vitess/go/vt/log" diff --git a/go/vt/vtgate/dynamicconfig/config.go b/go/vt/vtgate/dynamicconfig/config.go new file mode 100644 index 00000000000..5bb1d991eae --- /dev/null +++ b/go/vt/vtgate/dynamicconfig/config.go @@ -0,0 +1,6 @@ +package dynamicconfig + +type DDL interface { + OnlineEnabled() bool + DirectEnabled() bool +} diff --git a/go/vt/vtgate/engine/cached_size.go b/go/vt/vtgate/engine/cached_size.go index 4c0d1009bd1..c764a6aab08 100644 --- a/go/vt/vtgate/engine/cached_size.go +++ b/go/vt/vtgate/engine/cached_size.go @@ -131,7 +131,7 @@ func (cached *DDL) CachedSize(alloc bool) int64 { } size := int64(0) if alloc { - size += int64(64) + size += int64(80) } // field Keyspace *vitess.io/vitess/go/vt/vtgate/vindexes.Keyspace size += cached.Keyspace.CachedSize(true) @@ -145,6 +145,10 @@ func (cached *DDL) CachedSize(alloc bool) int64 { size += cached.NormalDDL.CachedSize(true) // field OnlineDDL *vitess.io/vitess/go/vt/vtgate/engine.OnlineDDL size += cached.OnlineDDL.CachedSize(true) + // field Config vitess.io/vitess/go/vt/vtgate/dynamicconfig.DDL + if cc, ok := cached.Config.(cachedObject); ok { + size += cc.CachedSize(true) + } return size } func (cached *DML) CachedSize(alloc bool) int64 { diff --git a/go/vt/vtgate/engine/ddl.go b/go/vt/vtgate/engine/ddl.go index cfdaa5866dc..d7e17eb4f4f 100644 --- a/go/vt/vtgate/engine/ddl.go +++ b/go/vt/vtgate/engine/ddl.go @@ -25,6 +25,7 @@ import ( "vitess.io/vitess/go/vt/schema" "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vterrors" + "vitess.io/vitess/go/vt/vtgate/dynamicconfig" "vitess.io/vitess/go/vt/vtgate/vindexes" ) @@ -42,8 +43,7 @@ type DDL struct { NormalDDL *Send OnlineDDL *OnlineDDL - DirectDDLEnabled bool - OnlineDDLEnabled bool + Config dynamicconfig.DDL CreateTempTable bool } @@ -107,12 +107,12 @@ func (ddl *DDL) TryExecute(ctx context.Context, vcursor VCursor, bindVars map[st switch { case ddl.isOnlineSchemaDDL(): - if !ddl.OnlineDDLEnabled { + if !ddl.Config.OnlineEnabled() { return nil, schema.ErrOnlineDDLDisabled } return vcursor.ExecutePrimitive(ctx, ddl.OnlineDDL, bindVars, wantfields) default: // non online-ddl - if !ddl.DirectDDLEnabled { + if !ddl.Config.DirectEnabled() { return nil, schema.ErrDirectDDLDisabled } return vcursor.ExecutePrimitive(ctx, ddl.NormalDDL, bindVars, wantfields) diff --git a/go/vt/vtgate/engine/ddl_test.go b/go/vt/vtgate/engine/ddl_test.go index 3f7ccb75f70..1d52089bf39 100644 --- a/go/vt/vtgate/engine/ddl_test.go +++ b/go/vt/vtgate/engine/ddl_test.go @@ -27,13 +27,23 @@ import ( "vitess.io/vitess/go/vt/vtgate/vindexes" ) +type ddlConfig struct{} + +func (ddlConfig) DirectEnabled() bool { + return true +} + +func (ddlConfig) OnlineEnabled() bool { + return true +} + func TestDDL(t *testing.T) { ddl := &DDL{ DDL: &sqlparser.CreateTable{ Table: sqlparser.NewTableName("a"), }, - DirectDDLEnabled: true, - OnlineDDL: &OnlineDDL{}, + Config: ddlConfig{}, + OnlineDDL: &OnlineDDL{}, NormalDDL: &Send{ Keyspace: &vindexes.Keyspace{ Name: "ks", diff --git a/go/vt/vtgate/evalengine/eval_result.go b/go/vt/vtgate/evalengine/eval_result.go index d9916af03be..5c1973d8eb1 100644 --- a/go/vt/vtgate/evalengine/eval_result.go +++ b/go/vt/vtgate/evalengine/eval_result.go @@ -62,6 +62,7 @@ func (er EvalResult) String() string { // TupleValues allows for retrieval of the value we expose for public consumption func (er EvalResult) TupleValues() []sqltypes.Value { + // TODO: Make this collation-aware switch v := er.v.(type) { case *evalTuple: result := make([]sqltypes.Value, 0, len(v.t)) diff --git a/go/vt/vtgate/executor.go b/go/vt/vtgate/executor.go index 928f42fca30..e84ab7fbb21 100644 --- a/go/vt/vtgate/executor.go +++ b/go/vt/vtgate/executor.go @@ -30,6 +30,8 @@ import ( "github.com/spf13/pflag" + vschemapb "vitess.io/vitess/go/vt/proto/vschema" + "vitess.io/vitess/go/acl" "vitess.io/vitess/go/cache/theine" "vitess.io/vitess/go/mysql/capabilities" @@ -57,6 +59,7 @@ import ( "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vtgate/engine" "vitess.io/vitess/go/vt/vtgate/evalengine" + econtext "vitess.io/vitess/go/vt/vtgate/executorcontext" "vitess.io/vitess/go/vt/vtgate/logstats" "vitess.io/vitess/go/vt/vtgate/planbuilder" "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" @@ -67,7 +70,6 @@ import ( ) var ( - errNoKeyspace = vterrors.VT09005() defaultTabletType = topodatapb.TabletType_PRIMARY // TODO: @rafael - These two counters should be deprecated in favor of the ByTable ones in v17+. They are kept for now for backwards compatibility. @@ -111,7 +113,6 @@ type Executor struct { resolver *Resolver scatterConn *ScatterConn txConn *TxConn - pv plancontext.PlannerVersion mu sync.Mutex vschema *vindexes.VSchema @@ -121,8 +122,7 @@ type Executor struct { plans *PlanCache epoch atomic.Uint32 - normalize bool - warnShardedOnly bool + normalize bool vm *VSchemaManager schemaTracker SchemaInfo @@ -135,6 +135,8 @@ type Executor struct { warmingReadsPercent int warmingReadsChannel chan bool + + vConfig econtext.VCursorConfig } var executorOnce sync.Once @@ -175,15 +177,15 @@ func NewExecutor( scatterConn: resolver.scatterConn, txConn: resolver.scatterConn.txConn, normalize: normalize, - warnShardedOnly: warnOnShardedOnly, streamSize: streamSize, schemaTracker: schemaTracker, allowScatter: !noScatter, - pv: pv, plans: plans, warmingReadsPercent: warmingReadsPercent, warmingReadsChannel: make(chan bool, warmingReadsConcurrency), } + // setting the vcursor config. + e.initVConfig(warnOnShardedOnly, pv) vschemaacl.Init() // we subscribe to update from the VSchemaManager @@ -223,7 +225,7 @@ func NewExecutor( } // Execute executes a non-streaming query. -func (e *Executor) Execute(ctx context.Context, mysqlCtx vtgateservice.MySQLConnection, method string, safeSession *SafeSession, sql string, bindVars map[string]*querypb.BindVariable) (result *sqltypes.Result, err error) { +func (e *Executor) Execute(ctx context.Context, mysqlCtx vtgateservice.MySQLConnection, method string, safeSession *econtext.SafeSession, sql string, bindVars map[string]*querypb.BindVariable) (result *sqltypes.Result, err error) { span, ctx := trace.NewSpan(ctx, "executor.Execute") span.Annotate("method", method) trace.AnnotateSQL(span, sqlparser.Preview(sql)) @@ -286,7 +288,7 @@ func (e *Executor) StreamExecute( ctx context.Context, mysqlCtx vtgateservice.MySQLConnection, method string, - safeSession *SafeSession, + safeSession *econtext.SafeSession, sql string, bindVars map[string]*querypb.BindVariable, callback func(*sqltypes.Result) error, @@ -300,7 +302,7 @@ func (e *Executor) StreamExecute( srr := &streaminResultReceiver{callback: callback} var err error - resultHandler := func(ctx context.Context, plan *engine.Plan, vc *vcursorImpl, bindVars map[string]*querypb.BindVariable, execStart time.Time) error { + resultHandler := func(ctx context.Context, plan *engine.Plan, vc *econtext.VCursorImpl, bindVars map[string]*querypb.BindVariable, execStart time.Time) error { var seenResults atomic.Bool var resultMu sync.Mutex result := &sqltypes.Result{} @@ -368,7 +370,7 @@ func (e *Executor) StreamExecute( logStats.TablesUsed = plan.TablesUsed logStats.TabletType = vc.TabletType().String() logStats.ExecuteTime = time.Since(execStart) - logStats.ActiveKeyspace = vc.keyspace + logStats.ActiveKeyspace = vc.GetKeyspace() e.updateQueryCounts(plan.Instructions.RouteType(), plan.Instructions.GetKeyspaceName(), plan.Instructions.GetTableName(), int64(logStats.ShardQueries)) @@ -411,12 +413,12 @@ func canReturnRows(stmtType sqlparser.StatementType) bool { } } -func saveSessionStats(safeSession *SafeSession, stmtType sqlparser.StatementType, rowsAffected, insertID uint64, rowsReturned int, err error) { +func saveSessionStats(safeSession *econtext.SafeSession, stmtType sqlparser.StatementType, rowsAffected, insertID uint64, rowsReturned int, err error) { safeSession.RowCount = -1 if err != nil { return } - if !safeSession.foundRowsHandled { + if !safeSession.IsFoundRowsHandled() { safeSession.FoundRows = uint64(rowsReturned) } if insertID > 0 { @@ -430,11 +432,11 @@ func saveSessionStats(safeSession *SafeSession, stmtType sqlparser.StatementType } } -func (e *Executor) execute(ctx context.Context, mysqlCtx vtgateservice.MySQLConnection, safeSession *SafeSession, sql string, bindVars map[string]*querypb.BindVariable, logStats *logstats.LogStats) (sqlparser.StatementType, *sqltypes.Result, error) { +func (e *Executor) execute(ctx context.Context, mysqlCtx vtgateservice.MySQLConnection, safeSession *econtext.SafeSession, sql string, bindVars map[string]*querypb.BindVariable, logStats *logstats.LogStats) (sqlparser.StatementType, *sqltypes.Result, error) { var err error var qr *sqltypes.Result var stmtType sqlparser.StatementType - err = e.newExecute(ctx, mysqlCtx, safeSession, sql, bindVars, logStats, func(ctx context.Context, plan *engine.Plan, vc *vcursorImpl, bindVars map[string]*querypb.BindVariable, time time.Time) error { + err = e.newExecute(ctx, mysqlCtx, safeSession, sql, bindVars, logStats, func(ctx context.Context, plan *engine.Plan, vc *econtext.VCursorImpl, bindVars map[string]*querypb.BindVariable, time time.Time) error { stmtType = plan.Type qr, err = e.executePlan(ctx, safeSession, plan, vc, bindVars, logStats, time) return err @@ -448,7 +450,7 @@ func (e *Executor) execute(ctx context.Context, mysqlCtx vtgateservice.MySQLConn } // addNeededBindVars adds bind vars that are needed by the plan -func (e *Executor) addNeededBindVars(vcursor *vcursorImpl, bindVarNeeds *sqlparser.BindVarNeeds, bindVars map[string]*querypb.BindVariable, session *SafeSession) error { +func (e *Executor) addNeededBindVars(vcursor *econtext.VCursorImpl, bindVarNeeds *sqlparser.BindVarNeeds, bindVars map[string]*querypb.BindVariable, session *econtext.SafeSession) error { for _, funcName := range bindVarNeeds.NeedFunctionResult { switch funcName { case sqlparser.DBVarName: @@ -541,7 +543,7 @@ func (e *Executor) addNeededBindVars(vcursor *vcursorImpl, bindVarNeeds *sqlpars } evalExpr, err := evalengine.Translate(expr, &evalengine.Config{ - Collation: vcursor.collation, + Collation: vcursor.ConnCollation(), Environment: e.env, SQLMode: evalengine.ParseSQLMode(vcursor.SQLMode()), }) @@ -552,7 +554,7 @@ func (e *Executor) addNeededBindVars(vcursor *vcursorImpl, bindVarNeeds *sqlpars if err != nil { return err } - bindVars[key] = sqltypes.ValueBindVariable(evaluated.Value(vcursor.collation)) + bindVars[key] = sqltypes.ValueBindVariable(evaluated.Value(vcursor.ConnCollation())) } } } @@ -572,21 +574,21 @@ func (e *Executor) addNeededBindVars(vcursor *vcursorImpl, bindVarNeeds *sqlpars return nil } -func ifOptionsExist(session *SafeSession, f func(*querypb.ExecuteOptions)) { +func ifOptionsExist(session *econtext.SafeSession, f func(*querypb.ExecuteOptions)) { options := session.GetOptions() if options != nil { f(options) } } -func ifReadAfterWriteExist(session *SafeSession, f func(*vtgatepb.ReadAfterWrite)) { +func ifReadAfterWriteExist(session *econtext.SafeSession, f func(*vtgatepb.ReadAfterWrite)) { raw := session.ReadAfterWrite if raw != nil { f(raw) } } -func (e *Executor) handleBegin(ctx context.Context, safeSession *SafeSession, logStats *logstats.LogStats, stmt sqlparser.Statement) (*sqltypes.Result, error) { +func (e *Executor) handleBegin(ctx context.Context, safeSession *econtext.SafeSession, logStats *logstats.LogStats, stmt sqlparser.Statement) (*sqltypes.Result, error) { execStart := time.Now() logStats.PlanTime = execStart.Sub(logStats.StartTime) @@ -599,7 +601,7 @@ func (e *Executor) handleBegin(ctx context.Context, safeSession *SafeSession, lo return &sqltypes.Result{}, err } -func (e *Executor) handleCommit(ctx context.Context, safeSession *SafeSession, logStats *logstats.LogStats) (*sqltypes.Result, error) { +func (e *Executor) handleCommit(ctx context.Context, safeSession *econtext.SafeSession, logStats *logstats.LogStats) (*sqltypes.Result, error) { execStart := time.Now() logStats.PlanTime = execStart.Sub(logStats.StartTime) logStats.ShardQueries = uint64(len(safeSession.ShardSessions)) @@ -611,11 +613,11 @@ func (e *Executor) handleCommit(ctx context.Context, safeSession *SafeSession, l } // Commit commits the existing transactions -func (e *Executor) Commit(ctx context.Context, safeSession *SafeSession) error { +func (e *Executor) Commit(ctx context.Context, safeSession *econtext.SafeSession) error { return e.txConn.Commit(ctx, safeSession) } -func (e *Executor) handleRollback(ctx context.Context, safeSession *SafeSession, logStats *logstats.LogStats) (*sqltypes.Result, error) { +func (e *Executor) handleRollback(ctx context.Context, safeSession *econtext.SafeSession, logStats *logstats.LogStats) (*sqltypes.Result, error) { execStart := time.Now() logStats.PlanTime = execStart.Sub(logStats.StartTime) logStats.ShardQueries = uint64(len(safeSession.ShardSessions)) @@ -625,7 +627,7 @@ func (e *Executor) handleRollback(ctx context.Context, safeSession *SafeSession, return &sqltypes.Result{}, err } -func (e *Executor) handleSavepoint(ctx context.Context, safeSession *SafeSession, sql string, planType string, logStats *logstats.LogStats, nonTxResponse func(query string) (*sqltypes.Result, error), ignoreMaxMemoryRows bool) (*sqltypes.Result, error) { +func (e *Executor) handleSavepoint(ctx context.Context, safeSession *econtext.SafeSession, sql string, planType string, logStats *logstats.LogStats, nonTxResponse func(query string) (*sqltypes.Result, error), ignoreMaxMemoryRows bool) (*sqltypes.Result, error) { execStart := time.Now() logStats.PlanTime = execStart.Sub(logStats.StartTime) logStats.ShardQueries = uint64(len(safeSession.ShardSessions)) @@ -637,7 +639,7 @@ func (e *Executor) handleSavepoint(ctx context.Context, safeSession *SafeSession // If no transaction exists on any of the shard sessions, // then savepoint does not need to be executed, it will be only stored in the session // and later will be executed when a transaction is started. - if !safeSession.isTxOpen() { + if !safeSession.IsTxOpen() { if safeSession.InTransaction() { // Storing, as this needs to be executed just after starting transaction on the shard. safeSession.StoreSavepoint(sql) @@ -645,7 +647,7 @@ func (e *Executor) handleSavepoint(ctx context.Context, safeSession *SafeSession } return nonTxResponse(sql) } - orig := safeSession.commitOrder + orig := safeSession.GetCommitOrder() qr, err := e.executeSPInAllSessions(ctx, safeSession, sql, ignoreMaxMemoryRows) safeSession.SetCommitOrder(orig) if err != nil { @@ -657,7 +659,7 @@ func (e *Executor) handleSavepoint(ctx context.Context, safeSession *SafeSession // executeSPInAllSessions function executes the savepoint query in all open shard sessions (pre, normal and post) // which has non-zero transaction id (i.e. an open transaction on the shard connection). -func (e *Executor) executeSPInAllSessions(ctx context.Context, safeSession *SafeSession, sql string, ignoreMaxMemoryRows bool) (*sqltypes.Result, error) { +func (e *Executor) executeSPInAllSessions(ctx context.Context, safeSession *econtext.SafeSession, sql string, ignoreMaxMemoryRows bool) (*sqltypes.Result, error) { var qr *sqltypes.Result var errs []error for _, co := range []vtgatepb.CommitOrder{vtgatepb.CommitOrder_PRE, vtgatepb.CommitOrder_NORMAL, vtgatepb.CommitOrder_POST} { @@ -665,7 +667,7 @@ func (e *Executor) executeSPInAllSessions(ctx context.Context, safeSession *Safe var rss []*srvtopo.ResolvedShard var queries []*querypb.BoundQuery - for _, shardSession := range safeSession.getSessions() { + for _, shardSession := range safeSession.GetSessions() { // This will avoid executing savepoint on reserved connections // which has no open transaction. if shardSession.TransactionId == 0 { @@ -718,11 +720,11 @@ func (e *Executor) handleKill(ctx context.Context, mysqlCtx vtgateservice.MySQLC // CloseSession releases the current connection, which rollbacks open transactions and closes reserved connections. // It is called then the MySQL servers closes the connection to its client. -func (e *Executor) CloseSession(ctx context.Context, safeSession *SafeSession) error { +func (e *Executor) CloseSession(ctx context.Context, safeSession *econtext.SafeSession) error { return e.txConn.ReleaseAll(ctx, safeSession) } -func (e *Executor) setVitessMetadata(ctx context.Context, name, value string) error { +func (e *Executor) SetVitessMetadata(ctx context.Context, name, value string) error { // TODO(kalfonso): move to its own acl check and consolidate into an acl component that can handle multiple operations (vschema, metadata) user := callerid.ImmediateCallerIDFromContext(ctx) allowed := vschemaacl.Authorized(user) @@ -741,7 +743,7 @@ func (e *Executor) setVitessMetadata(ctx context.Context, name, value string) er return ts.UpsertMetadata(ctx, name, value) } -func (e *Executor) showVitessMetadata(ctx context.Context, filter *sqlparser.ShowFilter) (*sqltypes.Result, error) { +func (e *Executor) ShowVitessMetadata(ctx context.Context, filter *sqlparser.ShowFilter) (*sqltypes.Result, error) { ts, err := e.serv.GetTopoServer() if err != nil { return nil, err @@ -774,7 +776,7 @@ func (e *Executor) showVitessMetadata(ctx context.Context, filter *sqlparser.Sho type tabletFilter func(tablet *topodatapb.Tablet, servingState string, primaryTermStartTime int64) bool -func (e *Executor) showShards(ctx context.Context, filter *sqlparser.ShowFilter, destTabletType topodatapb.TabletType) (*sqltypes.Result, error) { +func (e *Executor) ShowShards(ctx context.Context, filter *sqlparser.ShowFilter, destTabletType topodatapb.TabletType) (*sqltypes.Result, error) { showVitessShardsFilters := func(filter *sqlparser.ShowFilter) ([]func(string) bool, []func(string, *topodatapb.ShardReference) bool) { keyspaceFilters := []func(string) bool{} shardFilters := []func(string, *topodatapb.ShardReference) bool{} @@ -858,7 +860,7 @@ func (e *Executor) showShards(ctx context.Context, filter *sqlparser.ShowFilter, }, nil } -func (e *Executor) showTablets(filter *sqlparser.ShowFilter) (*sqltypes.Result, error) { +func (e *Executor) ShowTablets(filter *sqlparser.ShowFilter) (*sqltypes.Result, error) { getTabletFilters := func(filter *sqlparser.ShowFilter) []tabletFilter { var filters []tabletFilter @@ -931,7 +933,7 @@ func (e *Executor) showTablets(filter *sqlparser.ShowFilter) (*sqltypes.Result, }, nil } -func (e *Executor) showVitessReplicationStatus(ctx context.Context, filter *sqlparser.ShowFilter) (*sqltypes.Result, error) { +func (e *Executor) ShowVitessReplicationStatus(ctx context.Context, filter *sqlparser.ShowFilter) (*sqltypes.Result, error) { ctx, cancel := context.WithTimeout(ctx, healthCheckTimeout) defer cancel() rows := [][]sqltypes.Value{} @@ -1078,26 +1080,14 @@ func (e *Executor) SaveVSchema(vschema *vindexes.VSchema, stats *VSchemaStats) { // ParseDestinationTarget parses destination target string and sets default keyspace if possible. func (e *Executor) ParseDestinationTarget(targetString string) (string, topodatapb.TabletType, key.Destination, error) { - destKeyspace, destTabletType, dest, err := topoproto.ParseDestination(targetString, defaultTabletType) - // Set default keyspace - if destKeyspace == "" && len(e.VSchema().Keyspaces) == 1 { - for k := range e.VSchema().Keyspaces { - destKeyspace = k - } - } - return destKeyspace, destTabletType, dest, err -} - -type iQueryOption interface { - cachePlan() bool - getSelectLimit() int + return econtext.ParseDestinationTarget(targetString, defaultTabletType, e.VSchema()) } // getPlan computes the plan for the given query. If one is in // the cache, it reuses it. func (e *Executor) getPlan( ctx context.Context, - vcursor *vcursorImpl, + vcursor *econtext.VCursorImpl, query string, stmt sqlparser.Statement, comments sqlparser.MarginComments, @@ -1135,10 +1125,10 @@ func (e *Executor) getPlan( reservedVars, bindVars, parameterize, - vcursor.keyspace, - vcursor.safeSession.getSelectLimit(), + vcursor.GetKeyspace(), + vcursor.SafeSession.GetSelectLimit(), setVarComment, - vcursor.safeSession.SystemVariables, + vcursor.GetSystemVariablesCopy(), vcursor.GetForeignKeyChecksState(), vcursor, ) @@ -1157,9 +1147,9 @@ func (e *Executor) getPlan( return e.cacheAndBuildStatement(ctx, vcursor, query, stmt, reservedVars, bindVarNeeds, logStats) } -func (e *Executor) hashPlan(ctx context.Context, vcursor *vcursorImpl, query string) PlanCacheKey { +func (e *Executor) hashPlan(ctx context.Context, vcursor *econtext.VCursorImpl, query string) PlanCacheKey { hasher := vthash.New256() - vcursor.keyForPlan(ctx, query, hasher) + vcursor.KeyForPlan(ctx, query, hasher) var planKey PlanCacheKey hasher.Sum(planKey[:0]) @@ -1168,19 +1158,22 @@ func (e *Executor) hashPlan(ctx context.Context, vcursor *vcursorImpl, query str func (e *Executor) buildStatement( ctx context.Context, - vcursor *vcursorImpl, + vcursor *econtext.VCursorImpl, query string, stmt sqlparser.Statement, reservedVars *sqlparser.ReservedVars, bindVarNeeds *sqlparser.BindVarNeeds, ) (*engine.Plan, error) { - plan, err := planbuilder.BuildFromStmt(ctx, query, stmt, reservedVars, vcursor, bindVarNeeds, enableOnlineDDL, enableDirectDDL) + cfg := &dynamicViperConfig{ + onlineDDL: enableOnlineDDL, + directDDL: enableDirectDDL, + } + plan, err := planbuilder.BuildFromStmt(ctx, query, stmt, reservedVars, vcursor, bindVarNeeds, cfg) if err != nil { return nil, err } - plan.Warnings = vcursor.warnings - vcursor.warnings = nil + plan.Warnings = vcursor.GetAndEmptyWarnings() err = e.checkThatPlanIsValid(stmt, plan) return plan, err @@ -1188,14 +1181,14 @@ func (e *Executor) buildStatement( func (e *Executor) cacheAndBuildStatement( ctx context.Context, - vcursor *vcursorImpl, + vcursor *econtext.VCursorImpl, query string, stmt sqlparser.Statement, reservedVars *sqlparser.ReservedVars, bindVarNeeds *sqlparser.BindVarNeeds, logStats *logstats.LogStats, ) (*engine.Plan, error) { - planCachable := sqlparser.CachePlan(stmt) && vcursor.safeSession.cachePlan() + planCachable := sqlparser.CachePlan(stmt) && vcursor.CachePlan() if planCachable { planKey := e.hashPlan(ctx, vcursor, query) @@ -1213,7 +1206,7 @@ func (e *Executor) canNormalizeStatement(stmt sqlparser.Statement, setVarComment return sqlparser.CanNormalize(stmt) || setVarComment != "" } -func prepareSetVarComment(vcursor *vcursorImpl, stmt sqlparser.Statement) (string, error) { +func prepareSetVarComment(vcursor *econtext.VCursorImpl, stmt sqlparser.Statement) (string, error) { if vcursor == nil || vcursor.Session().InReservedConn() { return "", nil } @@ -1354,7 +1347,7 @@ func isValidPayloadSize(query string) bool { } // Prepare executes a prepare statements. -func (e *Executor) Prepare(ctx context.Context, method string, safeSession *SafeSession, sql string, bindVars map[string]*querypb.BindVariable) (fld []*querypb.Field, err error) { +func (e *Executor) Prepare(ctx context.Context, method string, safeSession *econtext.SafeSession, sql string, bindVars map[string]*querypb.BindVariable) (fld []*querypb.Field, err error) { logStats := logstats.NewLogStats(ctx, method, sql, safeSession.GetSessionUUID(), bindVars) fld, err = e.prepare(ctx, safeSession, sql, bindVars, logStats) logStats.Error = err @@ -1373,7 +1366,7 @@ func (e *Executor) Prepare(ctx context.Context, method string, safeSession *Safe return fld, err } -func (e *Executor) prepare(ctx context.Context, safeSession *SafeSession, sql string, bindVars map[string]*querypb.BindVariable, logStats *logstats.LogStats) ([]*querypb.Field, error) { +func (e *Executor) prepare(ctx context.Context, safeSession *econtext.SafeSession, sql string, bindVars map[string]*querypb.BindVariable, logStats *logstats.LogStats) ([]*querypb.Field, error) { // Start an implicit transaction if necessary. if !safeSession.Autocommit && !safeSession.InTransaction() { if err := e.txConn.Begin(ctx, safeSession, nil); err != nil { @@ -1409,9 +1402,41 @@ func (e *Executor) prepare(ctx context.Context, safeSession *SafeSession, sql st return nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "[BUG] unrecognized prepare statement: %s", sql) } -func (e *Executor) handlePrepare(ctx context.Context, safeSession *SafeSession, sql string, bindVars map[string]*querypb.BindVariable, logStats *logstats.LogStats) ([]*querypb.Field, error) { +func (e *Executor) initVConfig(warnOnShardedOnly bool, pv plancontext.PlannerVersion) { + connCollation := collations.Unknown + if gw, isTabletGw := e.resolver.resolver.GetGateway().(*TabletGateway); isTabletGw { + connCollation = gw.DefaultConnCollation() + } + if connCollation == collations.Unknown { + connCollation = e.env.CollationEnv().DefaultConnectionCharset() + } + + e.vConfig = econtext.VCursorConfig{ + Collation: connCollation, + DefaultTabletType: defaultTabletType, + PlannerVersion: pv, + + QueryTimeout: queryTimeout, + MaxMemoryRows: maxMemoryRows, + + SetVarEnabled: sysVarSetEnabled, + EnableViews: enableViews, + ForeignKeyMode: fkMode(foreignKeyMode), + EnableShardRouting: enableShardRouting, + WarnShardedOnly: warnOnShardedOnly, + + DBDDLPlugin: dbDDLPlugin, + + WarmingReadsPercent: e.warmingReadsPercent, + WarmingReadsTimeout: warmingReadsQueryTimeout, + WarmingReadsChannel: e.warmingReadsChannel, + } +} + +func (e *Executor) handlePrepare(ctx context.Context, safeSession *econtext.SafeSession, sql string, bindVars map[string]*querypb.BindVariable, logStats *logstats.LogStats) ([]*querypb.Field, error) { query, comments := sqlparser.SplitMarginComments(sql) - vcursor, _ := newVCursorImpl(safeSession, comments, e, logStats, e.vm, e.VSchema(), e.resolver.resolver, e.serv, e.warnShardedOnly, e.pv) + + vcursor, _ := econtext.NewVCursorImpl(safeSession, comments, e, logStats, e.vm, e.VSchema(), e.resolver.resolver, e.serv, nullResultsObserver{}, e.vConfig) stmt, reservedVars, err := parseAndValidateQuery(query, e.env.Parser()) if err != nil { @@ -1460,17 +1485,17 @@ func parseAndValidateQuery(query string, parser *sqlparser.Parser) (sqlparser.St } // ExecuteMultiShard implements the IExecutor interface -func (e *Executor) ExecuteMultiShard(ctx context.Context, primitive engine.Primitive, rss []*srvtopo.ResolvedShard, queries []*querypb.BoundQuery, session *SafeSession, autocommit bool, ignoreMaxMemoryRows bool, resultsObserver resultsObserver) (qr *sqltypes.Result, errs []error) { +func (e *Executor) ExecuteMultiShard(ctx context.Context, primitive engine.Primitive, rss []*srvtopo.ResolvedShard, queries []*querypb.BoundQuery, session *econtext.SafeSession, autocommit bool, ignoreMaxMemoryRows bool, resultsObserver econtext.ResultsObserver) (qr *sqltypes.Result, errs []error) { return e.scatterConn.ExecuteMultiShard(ctx, primitive, rss, queries, session, autocommit, ignoreMaxMemoryRows, resultsObserver) } // StreamExecuteMulti implements the IExecutor interface -func (e *Executor) StreamExecuteMulti(ctx context.Context, primitive engine.Primitive, query string, rss []*srvtopo.ResolvedShard, vars []map[string]*querypb.BindVariable, session *SafeSession, autocommit bool, callback func(reply *sqltypes.Result) error, resultsObserver resultsObserver) []error { +func (e *Executor) StreamExecuteMulti(ctx context.Context, primitive engine.Primitive, query string, rss []*srvtopo.ResolvedShard, vars []map[string]*querypb.BindVariable, session *econtext.SafeSession, autocommit bool, callback func(reply *sqltypes.Result) error, resultsObserver econtext.ResultsObserver) []error { return e.scatterConn.StreamExecuteMulti(ctx, primitive, query, rss, vars, session, autocommit, callback, resultsObserver) } // ExecuteLock implements the IExecutor interface -func (e *Executor) ExecuteLock(ctx context.Context, rs *srvtopo.ResolvedShard, query *querypb.BoundQuery, session *SafeSession, lockFuncType sqlparser.LockingFuncType) (*sqltypes.Result, error) { +func (e *Executor) ExecuteLock(ctx context.Context, rs *srvtopo.ResolvedShard, query *querypb.BoundQuery, session *econtext.SafeSession, lockFuncType sqlparser.LockingFuncType) (*sqltypes.Result, error) { return e.scatterConn.ExecuteLock(ctx, rs, query, session, lockFuncType) } @@ -1581,25 +1606,25 @@ func getTabletThrottlerStatus(tabletHostPort string) (string, error) { } // ReleaseLock implements the IExecutor interface -func (e *Executor) ReleaseLock(ctx context.Context, session *SafeSession) error { +func (e *Executor) ReleaseLock(ctx context.Context, session *econtext.SafeSession) error { return e.txConn.ReleaseLock(ctx, session) } -// planPrepareStmt implements the IExecutor interface -func (e *Executor) planPrepareStmt(ctx context.Context, vcursor *vcursorImpl, query string) (*engine.Plan, sqlparser.Statement, error) { +// PlanPrepareStmt implements the IExecutor interface +func (e *Executor) PlanPrepareStmt(ctx context.Context, vcursor *econtext.VCursorImpl, query string) (*engine.Plan, sqlparser.Statement, error) { stmt, reservedVars, err := parseAndValidateQuery(query, e.env.Parser()) if err != nil { return nil, nil, err } // creating this log stats to not interfere with the original log stats. - lStats := logstats.NewLogStats(ctx, "prepare", query, vcursor.safeSession.SessionUUID, nil) + lStats := logstats.NewLogStats(ctx, "prepare", query, vcursor.Session().GetSessionUUID(), nil) plan, err := e.getPlan( ctx, vcursor, query, sqlparser.Clone(stmt), - vcursor.marginComments, + vcursor.GetMarginComments(), map[string]*querypb.BindVariable{}, reservedVars, /* normalize */ false, @@ -1621,7 +1646,7 @@ func (e *Executor) Close() { e.plans.Close() } -func (e *Executor) environment() *vtenv.Environment { +func (e *Executor) Environment() *vtenv.Environment { return e.env } @@ -1633,6 +1658,10 @@ func (e *Executor) UnresolvedTransactions(ctx context.Context, targets []*queryp return e.txConn.UnresolvedTransactions(ctx, targets) } +func (e *Executor) AddWarningCount(name string, count int64) { + warnings.Add(name, count) +} + type ( errorTransformer interface { TransformError(err error) error @@ -1643,3 +1672,16 @@ type ( func (nullErrorTransformer) TransformError(err error) error { return err } + +func fkMode(foreignkey string) vschemapb.Keyspace_ForeignKeyMode { + switch foreignkey { + case "disallow": + return vschemapb.Keyspace_disallow + case "managed": + return vschemapb.Keyspace_managed + case "unmanaged": + return vschemapb.Keyspace_unmanaged + + } + return vschemapb.Keyspace_unspecified +} diff --git a/go/vt/vtgate/executor_ddl_test.go b/go/vt/vtgate/executor_ddl_test.go index 3274fd94475..bf117856e08 100644 --- a/go/vt/vtgate/executor_ddl_test.go +++ b/go/vt/vtgate/executor_ddl_test.go @@ -21,14 +21,15 @@ import ( "testing" vtgatepb "vitess.io/vitess/go/vt/proto/vtgate" + econtext "vitess.io/vitess/go/vt/vtgate/executorcontext" "github.com/stretchr/testify/require" ) func TestDDLFlags(t *testing.T) { defer func() { - enableOnlineDDL = true - enableDirectDDL = true + enableOnlineDDL.Set(true) + enableDirectDDL.Set(true) }() testcases := []struct { enableDirectDDL bool @@ -56,9 +57,9 @@ func TestDDLFlags(t *testing.T) { for _, testcase := range testcases { t.Run(fmt.Sprintf("%s-%v-%v", testcase.sql, testcase.enableDirectDDL, testcase.enableOnlineDDL), func(t *testing.T) { executor, _, _, _, ctx := createExecutorEnv(t) - session := NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded}) - enableDirectDDL = testcase.enableDirectDDL - enableOnlineDDL = testcase.enableOnlineDDL + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded}) + enableDirectDDL.Set(testcase.enableDirectDDL) + enableOnlineDDL.Set(testcase.enableOnlineDDL) _, err := executor.Execute(ctx, nil, "TestDDLFlags", session, testcase.sql, nil) if testcase.wantErr { require.EqualError(t, err, testcase.err) diff --git a/go/vt/vtgate/executor_dml_test.go b/go/vt/vtgate/executor_dml_test.go index 3dce4e212ef..792e197f48d 100644 --- a/go/vt/vtgate/executor_dml_test.go +++ b/go/vt/vtgate/executor_dml_test.go @@ -25,6 +25,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + econtext "vitess.io/vitess/go/vt/vtgate/executorcontext" + "vitess.io/vitess/go/mysql/config" "vitess.io/vitess/go/mysql/sqlerror" "vitess.io/vitess/go/sqltypes" @@ -135,7 +137,6 @@ func TestUpdateEqual(t *testing.T) { func TestUpdateFromSubQuery(t *testing.T) { executor, sbc1, sbc2, _, ctx := createExecutorEnv(t) - executor.pv = querypb.ExecuteOptions_Gen4 logChan := executor.queryLogger.Subscribe("Test") defer executor.queryLogger.Unsubscribe(logChan) @@ -234,7 +235,7 @@ func TestUpdateInTransactionLookupDefaultReadLock(t *testing.T) { )} executor, sbc1, sbc2, sbcLookup, ctx := createCustomExecutorSetValues(t, executorVSchema, res) - safeSession := NewSafeSession(&vtgatepb.Session{InTransaction: true}) + safeSession := econtext.NewSafeSession(&vtgatepb.Session{InTransaction: true}) _, err := executorExecSession(ctx, executor, "update t2_lookup set lu_col = 5 where nv_lu_col = 2", @@ -296,7 +297,7 @@ func TestUpdateInTransactionLookupExclusiveReadLock(t *testing.T) { )} executor, sbc1, sbc2, sbcLookup, ctx := createCustomExecutorSetValues(t, executorVSchema, res) - safeSession := NewSafeSession(&vtgatepb.Session{InTransaction: true}) + safeSession := econtext.NewSafeSession(&vtgatepb.Session{InTransaction: true}) _, err := executorExecSession(ctx, executor, "update t2_lookup set lu_col = 5 where erl_lu_col = 2", @@ -358,7 +359,7 @@ func TestUpdateInTransactionLookupSharedReadLock(t *testing.T) { )} executor, sbc1, sbc2, sbcLookup, ctx := createCustomExecutorSetValues(t, executorVSchema, res) - safeSession := NewSafeSession(&vtgatepb.Session{InTransaction: true}) + safeSession := econtext.NewSafeSession(&vtgatepb.Session{InTransaction: true}) _, err := executorExecSession(ctx, executor, "update t2_lookup set lu_col = 5 where srl_lu_col = 2", @@ -420,7 +421,7 @@ func TestUpdateInTransactionLookupNoReadLock(t *testing.T) { )} executor, sbc1, sbc2, sbcLookup, ctx := createCustomExecutorSetValues(t, executorVSchema, res) - safeSession := NewSafeSession(&vtgatepb.Session{InTransaction: true}) + safeSession := econtext.NewSafeSession(&vtgatepb.Session{InTransaction: true}) _, err := executorExecSession(ctx, executor, "update t2_lookup set lu_col = 5 where nrl_lu_col = 2", @@ -2066,7 +2067,7 @@ func TestInsertPartialFail1(t *testing.T) { context.Background(), nil, "TestExecute", - NewSafeSession(&vtgatepb.Session{InTransaction: true}), + econtext.NewSafeSession(&vtgatepb.Session{InTransaction: true}), "insert into user(id, v, name) values (1, 2, 'myname')", nil, ) @@ -2082,7 +2083,7 @@ func TestInsertPartialFail2(t *testing.T) { // Make the second DML fail, it should result in a rollback. sbc1.MustFailExecute[sqlparser.StmtInsert] = 1 - safeSession := NewSafeSession(&vtgatepb.Session{InTransaction: true}) + safeSession := econtext.NewSafeSession(&vtgatepb.Session{InTransaction: true}) _, err := executor.Execute( context.Background(), nil, @@ -2656,7 +2657,7 @@ func TestReservedConnDML(t *testing.T) { logChan := executor.queryLogger.Subscribe("TestReservedConnDML") defer executor.queryLogger.Unsubscribe(logChan) - session := NewAutocommitSession(&vtgatepb.Session{EnableSystemSettings: true}) + session := econtext.NewAutocommitSession(&vtgatepb.Session{EnableSystemSettings: true}) _, err := executor.Execute(ctx, nil, "TestReservedConnDML", session, "use "+KsTestUnsharded, nil) require.NoError(t, err) @@ -2708,7 +2709,7 @@ func TestStreamingDML(t *testing.T) { logChan := executor.queryLogger.Subscribe(method) defer executor.queryLogger.Unsubscribe(logChan) - session := NewAutocommitSession(&vtgatepb.Session{}) + session := econtext.NewAutocommitSession(&vtgatepb.Session{}) tcases := []struct { query string @@ -2792,7 +2793,7 @@ func TestPartialVindexInsertQueryFailure(t *testing.T) { logChan := executor.queryLogger.Subscribe("Test") defer executor.queryLogger.Unsubscribe(logChan) - session := NewAutocommitSession(&vtgatepb.Session{}) + session := econtext.NewAutocommitSession(&vtgatepb.Session{}) require.True(t, session.GetAutocommit()) require.False(t, session.InTransaction()) @@ -2845,7 +2846,7 @@ func TestPartialVindexInsertQueryFailureAutoCommit(t *testing.T) { logChan := executor.queryLogger.Subscribe("Test") defer executor.queryLogger.Unsubscribe(logChan) - session := NewAutocommitSession(&vtgatepb.Session{}) + session := econtext.NewAutocommitSession(&vtgatepb.Session{}) require.True(t, session.GetAutocommit()) require.False(t, session.InTransaction()) @@ -2886,7 +2887,7 @@ func TestPartialVindexInsertQueryFailureAutoCommit(t *testing.T) { func TestMultiInternalSavepoint(t *testing.T) { executor, sbc1, sbc2, _, ctx := createExecutorEnv(t) - session := NewAutocommitSession(&vtgatepb.Session{}) + session := econtext.NewAutocommitSession(&vtgatepb.Session{}) _, err := executorExecSession(ctx, executor, "begin", nil, session.Session) require.NoError(t, err) @@ -2935,7 +2936,7 @@ func TestInsertSelectFromDual(t *testing.T) { logChan := executor.queryLogger.Subscribe("TestInsertSelect") defer executor.queryLogger.Unsubscribe(logChan) - session := NewAutocommitSession(&vtgatepb.Session{}) + session := econtext.NewAutocommitSession(&vtgatepb.Session{}) query := "insert into user(id, v, name) select 1, 2, 'myname' from dual" wantQueries := []*querypb.BoundQuery{{ @@ -2990,7 +2991,7 @@ func TestInsertSelectFromTable(t *testing.T) { logChan := executor.queryLogger.Subscribe("TestInsertSelect") defer executor.queryLogger.Unsubscribe(logChan) - session := NewAutocommitSession(&vtgatepb.Session{}) + session := econtext.NewAutocommitSession(&vtgatepb.Session{}) query := "insert into user(id, name) select c1, c2 from music" wantQueries := []*querypb.BoundQuery{{ @@ -3140,3 +3141,62 @@ func TestDeleteMultiTable(t *testing.T) { // delete from `user` where (`user`.id) in ::dml_vals - 1 shard testQueryLog(t, executor, logChan, "TestExecute", "DELETE", "delete `user` from `user` join music on `user`.col = music.col where music.user_id = 1", 18) } + +// TestSessionRowsAffected test that rowsAffected is set correctly for each shard session. +func TestSessionRowsAffected(t *testing.T) { + method := t.Name() + executor, _, sbc4060, _, ctx := createExecutorEnv(t) + + session := econtext.NewAutocommitSession(&vtgatepb.Session{}) + + // start the transaction + _, err := executor.Execute(ctx, nil, method, session, "begin", nil) + require.NoError(t, err) + + // -20 - select query + _, err = executor.Execute(ctx, nil, method, session, "select * from user where id = 1", nil) + require.NoError(t, err) + require.Len(t, session.ShardSessions, 1) + require.False(t, session.ShardSessions[0].RowsAffected) + + // -20 - update query (rows affected) + _, err = executor.Execute(ctx, nil, method, session, "update user set foo = 41 where id = 1", nil) + require.NoError(t, err) + require.True(t, session.ShardSessions[0].RowsAffected) + + // e0- - select query + _, err = executor.Execute(ctx, nil, method, session, "select * from user where id = 7", nil) + require.NoError(t, err) + assert.Len(t, session.ShardSessions, 2) + require.False(t, session.ShardSessions[1].RowsAffected) + + // c0-e0 - update query (rows affected) + _, err = executor.Execute(ctx, nil, method, session, "update user set foo = 42 where id = 5", nil) + require.NoError(t, err) + require.Len(t, session.ShardSessions, 3) + require.True(t, session.ShardSessions[2].RowsAffected) + + // 40-60 - update query (no rows affected) + sbc4060.SetResults([]*sqltypes.Result{{RowsAffected: 0}}) + _, err = executor.Execute(ctx, nil, method, session, "update user set foo = 42 where id = 3", nil) + require.NoError(t, err) + assert.Len(t, session.ShardSessions, 4) + require.False(t, session.ShardSessions[3].RowsAffected) + + // 40-60 - select query + _, err = executor.Execute(ctx, nil, method, session, "select * from user where id = 3", nil) + require.NoError(t, err) + require.False(t, session.ShardSessions[3].RowsAffected) + + // 40-60 - delete query (rows affected) + _, err = executor.Execute(ctx, nil, method, session, "delete from user where id = 3", nil) + require.NoError(t, err) + require.True(t, session.ShardSessions[0].RowsAffected) + require.False(t, session.ShardSessions[1].RowsAffected) + require.True(t, session.ShardSessions[2].RowsAffected) + require.True(t, session.ShardSessions[3].RowsAffected) + + _, err = executor.Execute(ctx, nil, method, session, "commit", nil) + require.NoError(t, err) + require.Zero(t, session.ShardSessions) +} diff --git a/go/vt/vtgate/executor_framework_test.go b/go/vt/vtgate/executor_framework_test.go index 332139c4a78..2ee3425209f 100644 --- a/go/vt/vtgate/executor_framework_test.go +++ b/go/vt/vtgate/executor_framework_test.go @@ -28,6 +28,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + econtext "vitess.io/vitess/go/vt/vtgate/executorcontext" + "vitess.io/vitess/go/cache/theine" "vitess.io/vitess/go/constants/sidecar" "vitess.io/vitess/go/sqltypes" @@ -307,7 +309,7 @@ func executorExecSession(ctx context.Context, executor *Executor, sql string, bv ctx, nil, "TestExecute", - NewSafeSession(session), + econtext.NewSafeSession(session), sql, bv) } @@ -320,7 +322,7 @@ func executorPrepare(ctx context.Context, executor *Executor, session *vtgatepb. return executor.Prepare( ctx, "TestExecute", - NewSafeSession(session), + econtext.NewSafeSession(session), sql, bv) } @@ -331,7 +333,7 @@ func executorStream(ctx context.Context, executor *Executor, sql string) (qr *sq ctx, nil, "TestExecuteStream", - NewSafeSession(nil), + econtext.NewSafeSession(nil), sql, nil, func(qr *sqltypes.Result) error { diff --git a/go/vt/vtgate/executor_scatter_stats_test.go b/go/vt/vtgate/executor_scatter_stats_test.go index 84dd2744e8b..b665f850a23 100644 --- a/go/vt/vtgate/executor_scatter_stats_test.go +++ b/go/vt/vtgate/executor_scatter_stats_test.go @@ -24,12 +24,13 @@ import ( "github.com/stretchr/testify/require" vtgatepb "vitess.io/vitess/go/vt/proto/vtgate" + econtext "vitess.io/vitess/go/vt/vtgate/executorcontext" ) func TestScatterStatsWithNoScatterQuery(t *testing.T) { executor, _, _, _, ctx := createExecutorEnv(t) - session := NewSafeSession(&vtgatepb.Session{TargetString: "@primary"}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: "@primary"}) _, err := executor.Execute(ctx, nil, "TestExecutorResultsExceeded", session, "select * from main1", nil) require.NoError(t, err) @@ -41,7 +42,7 @@ func TestScatterStatsWithNoScatterQuery(t *testing.T) { func TestScatterStatsWithSingleScatterQuery(t *testing.T) { executor, _, _, _, ctx := createExecutorEnv(t) - session := NewSafeSession(&vtgatepb.Session{TargetString: "@primary"}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: "@primary"}) _, err := executor.Execute(ctx, nil, "TestExecutorResultsExceeded", session, "select * from user", nil) require.NoError(t, err) @@ -53,7 +54,7 @@ func TestScatterStatsWithSingleScatterQuery(t *testing.T) { func TestScatterStatsHttpWriting(t *testing.T) { executor, _, _, _, ctx := createExecutorEnv(t) - session := NewSafeSession(&vtgatepb.Session{TargetString: "@primary"}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: "@primary"}) _, err := executor.Execute(ctx, nil, "TestExecutorResultsExceeded", session, "select * from user", nil) require.NoError(t, err) diff --git a/go/vt/vtgate/executor_select_test.go b/go/vt/vtgate/executor_select_test.go index 8ba89d25daf..86aafaefba4 100644 --- a/go/vt/vtgate/executor_select_test.go +++ b/go/vt/vtgate/executor_select_test.go @@ -30,6 +30,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + econtext "vitess.io/vitess/go/vt/vtgate/executorcontext" + _flag "vitess.io/vitess/go/internal/flag" "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/sqltypes" @@ -59,7 +61,7 @@ func TestSelectNext(t *testing.T) { }} // Autocommit - session := NewAutocommitSession(&vtgatepb.Session{}) + session := econtext.NewAutocommitSession(&vtgatepb.Session{}) _, err := executor.Execute(context.Background(), nil, "TestSelectNext", session, query, bv) require.NoError(t, err) @@ -69,7 +71,7 @@ func TestSelectNext(t *testing.T) { sbclookup.Queries = nil // Txn - session = NewAutocommitSession(&vtgatepb.Session{}) + session = econtext.NewAutocommitSession(&vtgatepb.Session{}) session.Session.InTransaction = true _, err = executor.Execute(context.Background(), nil, "TestSelectNext", session, query, bv) require.NoError(t, err) @@ -80,7 +82,7 @@ func TestSelectNext(t *testing.T) { sbclookup.Queries = nil // Reserve - session = NewAutocommitSession(&vtgatepb.Session{}) + session = econtext.NewAutocommitSession(&vtgatepb.Session{}) session.Session.InReservedConn = true _, err = executor.Execute(context.Background(), nil, "TestSelectNext", session, query, bv) require.NoError(t, err) @@ -91,7 +93,7 @@ func TestSelectNext(t *testing.T) { sbclookup.Queries = nil // Reserve and Txn - session = NewAutocommitSession(&vtgatepb.Session{}) + session = econtext.NewAutocommitSession(&vtgatepb.Session{}) session.Session.InReservedConn = true session.Session.InTransaction = true _, err = executor.Execute(context.Background(), nil, "TestSelectNext", session, query, bv) @@ -107,7 +109,7 @@ func TestSelectDBA(t *testing.T) { query := "select * from INFORMATION_SCHEMA.foo" _, err := executor.Execute(context.Background(), nil, "TestSelectDBA", - NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}), + econtext.NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}), query, map[string]*querypb.BindVariable{}, ) require.NoError(t, err) @@ -117,7 +119,7 @@ func TestSelectDBA(t *testing.T) { sbc1.Queries = nil query = "SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES ist WHERE ist.table_schema = 'performance_schema' AND ist.table_name = 'foo'" _, err = executor.Execute(context.Background(), nil, "TestSelectDBA", - NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}), + econtext.NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}), query, map[string]*querypb.BindVariable{}, ) require.NoError(t, err) @@ -133,7 +135,7 @@ func TestSelectDBA(t *testing.T) { sbc1.Queries = nil query = "select 1 from information_schema.table_constraints where constraint_schema = 'vt_ks' and table_name = 'user'" _, err = executor.Execute(context.Background(), nil, "TestSelectDBA", - NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}), + econtext.NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}), query, map[string]*querypb.BindVariable{}, ) require.NoError(t, err) @@ -149,7 +151,7 @@ func TestSelectDBA(t *testing.T) { sbc1.Queries = nil query = "select 1 from information_schema.table_constraints where constraint_schema = 'vt_ks'" _, err = executor.Execute(context.Background(), nil, "TestSelectDBA", - NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}), + econtext.NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}), query, map[string]*querypb.BindVariable{}, ) require.NoError(t, err) @@ -167,7 +169,7 @@ func TestSystemVariablesMySQLBelow80(t *testing.T) { executor.normalize = true setVarEnabled = true - session := NewAutocommitSession(&vtgatepb.Session{EnableSystemSettings: true, TargetString: "TestExecutor"}) + session := econtext.NewAutocommitSession(&vtgatepb.Session{EnableSystemSettings: true, TargetString: "TestExecutor"}) sbc1.SetResults([]*sqltypes.Result{{ Fields: []*querypb.Field{ @@ -199,12 +201,8 @@ func TestSystemVariablesMySQLBelow80(t *testing.T) { func TestSystemVariablesWithSetVarDisabled(t *testing.T) { executor, sbc1, _, _, _ := createCustomExecutor(t, "{}", "8.0.0") executor.normalize = true - - setVarEnabled = false - defer func() { - setVarEnabled = true - }() - session := NewAutocommitSession(&vtgatepb.Session{EnableSystemSettings: true, TargetString: "TestExecutor"}) + executor.vConfig.SetVarEnabled = false + session := econtext.NewAutocommitSession(&vtgatepb.Session{EnableSystemSettings: true, TargetString: "TestExecutor"}) sbc1.SetResults([]*sqltypes.Result{{ Fields: []*querypb.Field{ @@ -237,7 +235,7 @@ func TestSetSystemVariablesTx(t *testing.T) { executor, sbc1, _, _, _ := createCustomExecutor(t, "{}", "8.0.1") executor.normalize = true - session := NewAutocommitSession(&vtgatepb.Session{EnableSystemSettings: true, TargetString: "TestExecutor"}) + session := econtext.NewAutocommitSession(&vtgatepb.Session{EnableSystemSettings: true, TargetString: "TestExecutor"}) _, err := executor.Execute(context.Background(), nil, "TestBegin", session, "begin", map[string]*querypb.BindVariable{}) require.NoError(t, err) @@ -283,7 +281,7 @@ func TestSetSystemVariables(t *testing.T) { executor, _, _, lookup, _ := createExecutorEnv(t) executor.normalize = true - session := NewAutocommitSession(&vtgatepb.Session{EnableSystemSettings: true, TargetString: KsTestUnsharded, SystemVariables: map[string]string{}}) + session := econtext.NewAutocommitSession(&vtgatepb.Session{EnableSystemSettings: true, TargetString: KsTestUnsharded, SystemVariables: map[string]string{}}) // Set @@sql_mode and execute a select statement. We should have SET_VAR in the select statement @@ -394,7 +392,7 @@ func TestSetSystemVariablesWithReservedConnection(t *testing.T) { executor, sbc1, _, _, _ := createExecutorEnv(t) executor.normalize = true - session := NewAutocommitSession(&vtgatepb.Session{EnableSystemSettings: true, SystemVariables: map[string]string{}}) + session := econtext.NewAutocommitSession(&vtgatepb.Session{EnableSystemSettings: true, SystemVariables: map[string]string{}}) sbc1.SetResults([]*sqltypes.Result{{ Fields: []*querypb.Field{ @@ -437,7 +435,7 @@ func TestSelectVindexFunc(t *testing.T) { executor, _, _, _, _ := createExecutorEnv(t) query := "select * from hash_index where id = 1" - session := NewAutocommitSession(&vtgatepb.Session{}) + session := econtext.NewAutocommitSession(&vtgatepb.Session{}) _, err := executor.Execute(context.Background(), nil, "TestSelectVindexFunc", session, query, nil) require.ErrorContains(t, err, "VT09005: no database selected") @@ -450,7 +448,7 @@ func TestCreateTableValidTimestamp(t *testing.T) { executor, sbc1, _, _, _ := createExecutorEnv(t) executor.normalize = true - session := NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor", SystemVariables: map[string]string{"sql_mode": "ALLOW_INVALID_DATES"}}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor", SystemVariables: map[string]string{"sql_mode": "ALLOW_INVALID_DATES"}}) query := "create table aa(t timestamp default 0)" _, err := executor.Execute(context.Background(), nil, "TestSelect", session, query, map[string]*querypb.BindVariable{}) @@ -468,11 +466,10 @@ func TestCreateTableValidTimestamp(t *testing.T) { func TestGen4SelectDBA(t *testing.T) { executor, sbc1, _, _, _ := createExecutorEnv(t) executor.normalize = true - executor.pv = querypb.ExecuteOptions_Gen4 query := "select * from INFORMATION_SCHEMA.TABLE_CONSTRAINTS" _, err := executor.Execute(context.Background(), nil, "TestSelectDBA", - NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}), + econtext.NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}), query, map[string]*querypb.BindVariable{}, ) require.NoError(t, err) @@ -483,7 +480,7 @@ func TestGen4SelectDBA(t *testing.T) { sbc1.Queries = nil query = "SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES ist WHERE ist.table_schema = 'performance_schema' AND ist.table_name = 'foo'" _, err = executor.Execute(context.Background(), nil, "TestSelectDBA", - NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}), + econtext.NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}), query, map[string]*querypb.BindVariable{}, ) require.NoError(t, err) @@ -501,7 +498,7 @@ func TestGen4SelectDBA(t *testing.T) { sbc1.Queries = nil query = "select 1 from information_schema.table_constraints where constraint_schema = 'vt_ks' and table_name = 'user'" _, err = executor.Execute(context.Background(), nil, "TestSelectDBA", - NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}), + econtext.NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}), query, map[string]*querypb.BindVariable{}, ) require.NoError(t, err) @@ -519,7 +516,7 @@ func TestGen4SelectDBA(t *testing.T) { sbc1.Queries = nil query = "select 1 from information_schema.table_constraints where constraint_schema = 'vt_ks'" - _, err = executor.Execute(context.Background(), nil, "TestSelectDBA", NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}), query, map[string]*querypb.BindVariable{}) + _, err = executor.Execute(context.Background(), nil, "TestSelectDBA", econtext.NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}), query, map[string]*querypb.BindVariable{}) require.NoError(t, err) wantQueries = []*querypb.BoundQuery{{ Sql: "select :vtg1 /* INT64 */ from information_schema.table_constraints where constraint_schema = :__vtschemaname /* VARCHAR */", @@ -534,7 +531,7 @@ func TestGen4SelectDBA(t *testing.T) { sbc1.Queries = nil query = "select t.table_schema,t.table_name,c.column_name,c.column_type from tables t join columns c on c.table_schema = t.table_schema and c.table_name = t.table_name where t.table_schema = 'TestExecutor' and c.table_schema = 'TestExecutor' order by t.table_schema,t.table_name,c.column_name" _, err = executor.Execute(context.Background(), nil, "TestSelectDBA", - NewSafeSession(&vtgatepb.Session{TargetString: "information_schema"}), + econtext.NewSafeSession(&vtgatepb.Session{TargetString: "information_schema"}), query, map[string]*querypb.BindVariable{}, ) require.NoError(t, err) @@ -651,7 +648,7 @@ func TestStreamBuffering(t *testing.T) { context.Background(), nil, "TestStreamBuffering", - NewSafeSession(session), + econtext.NewSafeSession(session), "select id from music_user_map where id = 1", nil, func(qr *sqltypes.Result) error { @@ -723,7 +720,7 @@ func TestStreamLimitOffset(t *testing.T) { context.Background(), nil, "TestStreamLimitOffset", - NewSafeSession(session), + econtext.NewSafeSession(session), "select id, textcol from user order by id limit 2 offset 2", nil, func(qr *sqltypes.Result) error { @@ -1083,7 +1080,7 @@ func TestSelectDatabase(t *testing.T) { newSession := &vtgatepb.Session{ TargetString: "@primary", } - session := NewSafeSession(newSession) + session := econtext.NewSafeSession(newSession) session.TargetString = "TestExecutor@primary" result, err := executor.Execute( context.Background(), @@ -1283,7 +1280,6 @@ func TestSelectEqual(t *testing.T) { func TestSelectINFromOR(t *testing.T) { executor, sbc1, _, _, ctx := createExecutorEnv(t) - executor.pv = querypb.ExecuteOptions_Gen4 session := &vtgatepb.Session{ TargetString: "@primary", @@ -2951,7 +2947,7 @@ func TestSubQueryAndQueryWithLimit(t *testing.T) { sbc1.SetResults(result1) sbc2.SetResults(result2) - exec(executor, NewSafeSession(&vtgatepb.Session{ + exec(executor, econtext.NewSafeSession(&vtgatepb.Session{ TargetString: "@primary", }), "select id1, id2 from t1 where id1 >= ( select id1 from t1 order by id1 asc limit 1) limit 100") require.Equal(t, 2, len(sbc1.Queries)) @@ -3000,7 +2996,7 @@ func TestSelectUsingMultiEqualOnLookupColumn(t *testing.T) { }}, }}) - result, err := exec(executor, NewSafeSession(&vtgatepb.Session{ + result, err := exec(executor, econtext.NewSafeSession(&vtgatepb.Session{ TargetString: KsTestSharded, }), "select nv_lu_col, other from t2_lookup WHERE (nv_lu_col = 1 AND other = 'bar') OR (nv_lu_col = 2 AND other = 'baz') OR (nv_lu_col = 3 AND other = 'qux') OR (nv_lu_col = 4 AND other = 'brz') OR (nv_lu_col = 5 AND other = 'brz')") @@ -3197,7 +3193,7 @@ func TestSelectWithUnionAll(t *testing.T) { func TestSelectLock(t *testing.T) { executor, sbc1, _, _, _ := createExecutorEnv(t) - session := NewSafeSession(nil) + session := econtext.NewSafeSession(nil) session.Session.InTransaction = true session.ShardSessions = []*vtgatepb.Session_ShardSession{{ Target: &querypb.Target{ @@ -3265,7 +3261,7 @@ func TestLockReserve(t *testing.T) { "select release_lock('lock name') from dual", } - session := NewAutocommitSession(&vtgatepb.Session{}) + session := econtext.NewAutocommitSession(&vtgatepb.Session{}) for _, sql := range tcases { t.Run(sql, func(t *testing.T) { @@ -3283,7 +3279,7 @@ func TestLockReserve(t *testing.T) { func TestSelectFromInformationSchema(t *testing.T) { executor, sbc1, _, _, _ := createExecutorEnv(t) - session := NewSafeSession(nil) + session := econtext.NewSafeSession(nil) // check failure when trying to query two keyspaces _, err := exec(executor, session, "SELECT B.TABLE_NAME FROM INFORMATION_SCHEMA.TABLES AS A, INFORMATION_SCHEMA.COLUMNS AS B WHERE A.TABLE_SCHEMA = 'TestExecutor' AND A.TABLE_SCHEMA = 'TestXBadSharding'") @@ -3410,8 +3406,8 @@ func TestSelectScatterFails(t *testing.T) { func TestGen4SelectStraightJoin(t *testing.T) { executor, sbc1, _, _, _ := createExecutorEnv(t) executor.normalize = true - executor.pv = querypb.ExecuteOptions_Gen4 - session := NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}) + + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}) query := "select u.id from user u straight_join user2 u2 on u.id = u2.id" _, err := executor.Execute(context.Background(), nil, "TestGen4SelectStraightJoin", @@ -3432,9 +3428,8 @@ func TestGen4SelectStraightJoin(t *testing.T) { func TestGen4MultiColumnVindexEqual(t *testing.T) { executor, sbc1, sbc2, _, _ := createExecutorEnv(t) executor.normalize = true - executor.pv = querypb.ExecuteOptions_Gen4 - session := NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}) query := "select * from user_region where cola = 1 and colb = 2" _, err := executor.Execute(context.Background(), nil, "TestGen4MultiColumnVindex", session, query, map[string]*querypb.BindVariable{}) require.NoError(t, err) @@ -3471,9 +3466,8 @@ func TestGen4MultiColumnVindexEqual(t *testing.T) { func TestGen4MultiColumnVindexIn(t *testing.T) { executor, sbc1, sbc2, _, _ := createExecutorEnv(t) executor.normalize = true - executor.pv = querypb.ExecuteOptions_Gen4 - session := NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}) query := "select * from user_region where cola IN (1,17984) and colb IN (2,3,4)" _, err := executor.Execute(context.Background(), nil, "TestGen4MultiColumnVindex", session, query, map[string]*querypb.BindVariable{}) require.NoError(t, err) @@ -3510,9 +3504,8 @@ func TestGen4MultiColumnVindexIn(t *testing.T) { func TestGen4MultiColMixedColComparision(t *testing.T) { executor, sbc1, sbc2, _, _ := createExecutorEnv(t) executor.normalize = true - executor.pv = querypb.ExecuteOptions_Gen4 - session := NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}) query := "select * from user_region where colb = 2 and cola IN (1,17984)" _, err := executor.Execute(context.Background(), nil, "TestGen4MultiColMixedColComparision", session, query, map[string]*querypb.BindVariable{}) require.NoError(t, err) @@ -3547,9 +3540,8 @@ func TestGen4MultiColMixedColComparision(t *testing.T) { func TestGen4MultiColBestVindexSel(t *testing.T) { executor, sbc1, sbc2, _, _ := createExecutorEnv(t) executor.normalize = true - executor.pv = querypb.ExecuteOptions_Gen4 - session := NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}) query := "select * from user_region where colb = 2 and cola IN (1,17984) and cola = 1" _, err := executor.Execute(context.Background(), nil, "TestGen4MultiColBestVindexSel", session, query, map[string]*querypb.BindVariable{}) require.NoError(t, err) @@ -3593,9 +3585,8 @@ func TestGen4MultiColBestVindexSel(t *testing.T) { func TestGen4MultiColMultiEqual(t *testing.T) { executor, sbc1, sbc2, _, _ := createExecutorEnv(t) executor.normalize = true - executor.pv = querypb.ExecuteOptions_Gen4 - session := NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}) query := "select * from user_region where (cola,colb) in ((17984,2),(17984,3))" _, err := executor.Execute(context.Background(), nil, "TestGen4MultiColMultiEqual", session, query, map[string]*querypb.BindVariable{}) require.NoError(t, err) @@ -3615,7 +3606,6 @@ func TestGen4MultiColMultiEqual(t *testing.T) { func TestGen4SelectUnqualifiedReferenceTable(t *testing.T) { executor, sbc1, sbc2, sbclookup, ctx := createExecutorEnv(t) - executor.pv = querypb.ExecuteOptions_Gen4 query := "select * from zip_detail" session := &vtgatepb.Session{ @@ -3636,7 +3626,6 @@ func TestGen4SelectUnqualifiedReferenceTable(t *testing.T) { func TestGen4SelectQualifiedReferenceTable(t *testing.T) { executor, sbc1, sbc2, sbclookup, ctx := createExecutorEnv(t) - executor.pv = querypb.ExecuteOptions_Gen4 query := fmt.Sprintf("select * from %s.zip_detail", KsTestSharded) session := &vtgatepb.Session{ @@ -3657,7 +3646,6 @@ func TestGen4SelectQualifiedReferenceTable(t *testing.T) { func TestGen4JoinUnqualifiedReferenceTable(t *testing.T) { executor, sbc1, sbc2, sbclookup, ctx := createExecutorEnv(t) - executor.pv = querypb.ExecuteOptions_Gen4 query := "select * from user join zip_detail on user.zip_detail_id = zip_detail.id" session := &vtgatepb.Session{ @@ -3694,7 +3682,6 @@ func TestGen4JoinUnqualifiedReferenceTable(t *testing.T) { func TestGen4CrossShardJoinQualifiedReferenceTable(t *testing.T) { executor, sbc1, sbc2, sbclookup, ctx := createExecutorEnv(t) - executor.pv = querypb.ExecuteOptions_Gen4 query := "select user.id from user join TestUnsharded.zip_detail on user.zip_detail_id = TestUnsharded.zip_detail.id" session := &vtgatepb.Session{ @@ -3751,7 +3738,6 @@ func TestRegionRange(t *testing.T) { } executor := createExecutor(ctx, serv, cell, resolver) defer executor.Close() - executor.pv = querypb.ExecuteOptions_Gen4 tcases := []struct { regionID int @@ -3769,7 +3755,7 @@ func TestRegionRange(t *testing.T) { for _, tcase := range tcases { t.Run(strconv.Itoa(tcase.regionID), func(t *testing.T) { sql := fmt.Sprintf("select * from user_region where cola = %d", tcase.regionID) - _, err := executor.Execute(context.Background(), nil, "TestRegionRange", NewAutocommitSession(&vtgatepb.Session{}), sql, nil) + _, err := executor.Execute(context.Background(), nil, "TestRegionRange", econtext.NewAutocommitSession(&vtgatepb.Session{}), sql, nil) require.NoError(t, err) count := 0 for _, sbc := range conns { @@ -3801,7 +3787,6 @@ func TestMultiCol(t *testing.T) { } executor := createExecutor(ctx, serv, cell, resolver) defer executor.Close() - executor.pv = querypb.ExecuteOptions_Gen4 tcases := []struct { cola, colb, colc int @@ -3817,7 +3802,7 @@ func TestMultiCol(t *testing.T) { shards: []string{"20a0-"}, }} - session := NewAutocommitSession(&vtgatepb.Session{}) + session := econtext.NewAutocommitSession(&vtgatepb.Session{}) for _, tcase := range tcases { t.Run(fmt.Sprintf("%d_%d_%d", tcase.cola, tcase.colb, tcase.colc), func(t *testing.T) { @@ -3882,7 +3867,6 @@ func TestMultiColPartial(t *testing.T) { } executor := createExecutor(ctx, serv, cell, resolver) defer executor.Close() - executor.pv = querypb.ExecuteOptions_Gen4 tcases := []struct { where string @@ -3907,7 +3891,7 @@ func TestMultiColPartial(t *testing.T) { shards: []string{"20a0c0-"}, }} - session := NewAutocommitSession(&vtgatepb.Session{}) + session := econtext.NewAutocommitSession(&vtgatepb.Session{}) for _, tcase := range tcases { t.Run(tcase.where, func(t *testing.T) { @@ -3946,7 +3930,6 @@ func TestSelectAggregationNoData(t *testing.T) { } executor := createExecutor(ctx, serv, cell, resolver) defer executor.Close() - executor.pv = querypb.ExecuteOptions_Gen4 tcases := []struct { sql string @@ -4038,7 +4021,6 @@ func TestSelectAggregationData(t *testing.T) { } executor := createExecutor(ctx, serv, cell, resolver) defer executor.Close() - executor.pv = querypb.ExecuteOptions_Gen4 tcases := []struct { sql string @@ -4196,8 +4178,7 @@ func TestSelectAggregationRandom(t *testing.T) { executor := createExecutor(ctx, serv, cell, resolver) defer executor.Close() - executor.pv = querypb.ExecuteOptions_Gen4 - session := NewAutocommitSession(&vtgatepb.Session{}) + session := econtext.NewAutocommitSession(&vtgatepb.Session{}) rs, err := executor.Execute(context.Background(), nil, "TestSelectCFC", session, "select /*vt+ PLANNER=gen4 */ A.a, A.b, (A.a / A.b) as c from (select sum(a) as a, sum(b) as b from user) A", nil) require.NoError(t, err) @@ -4207,7 +4188,7 @@ func TestSelectAggregationRandom(t *testing.T) { func TestSelectDateTypes(t *testing.T) { executor, _, _, _, _ := createExecutorEnv(t) executor.normalize = true - session := NewAutocommitSession(&vtgatepb.Session{}) + session := econtext.NewAutocommitSession(&vtgatepb.Session{}) qr, err := executor.Execute(context.Background(), nil, "TestSelectDateTypes", session, "select '2020-01-01' + interval month(date_sub(FROM_UNIXTIME(1234), interval 1 month))-1 month", nil) require.NoError(t, err) @@ -4218,7 +4199,7 @@ func TestSelectDateTypes(t *testing.T) { func TestSelectHexAndBit(t *testing.T) { executor, _, _, _, _ := createExecutorEnv(t) executor.normalize = true - session := NewAutocommitSession(&vtgatepb.Session{}) + session := econtext.NewAutocommitSession(&vtgatepb.Session{}) qr, err := executor.Execute(context.Background(), nil, "TestSelectHexAndBit", session, "select 0b1001, b'1001', 0x9, x'09'", nil) require.NoError(t, err) @@ -4234,7 +4215,7 @@ func TestSelectHexAndBit(t *testing.T) { func TestSelectCFC(t *testing.T) { executor, _, _, _, _ := createExecutorEnv(t) executor.normalize = true - session := NewAutocommitSession(&vtgatepb.Session{}) + session := econtext.NewAutocommitSession(&vtgatepb.Session{}) _, err := executor.Execute(context.Background(), nil, "TestSelectCFC", session, "select /*vt+ PLANNER=gen4 */ c2 from tbl_cfc where c1 like 'A%'", nil) require.NoError(t, err) @@ -4263,7 +4244,7 @@ func TestSelectView(t *testing.T) { require.NoError(t, err) executor.normalize = true - session := NewAutocommitSession(&vtgatepb.Session{}) + session := econtext.NewAutocommitSession(&vtgatepb.Session{}) _, err = executor.Execute(context.Background(), nil, "TestSelectView", session, "select * from user_details_view", nil) require.NoError(t, err) @@ -4304,7 +4285,7 @@ func TestWarmingReads(t *testing.T) { executor, primary, replica := createExecutorEnvWithPrimaryReplicaConn(t, ctx, 100) executor.normalize = true - session := NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded}) // Since queries on the replica will run in a separate go-routine, we need synchronization for the Queries field in the sandboxconn. replica.RequireQueriesLocking() @@ -4368,6 +4349,7 @@ func TestWarmingReads(t *testing.T) { // waitUntilQueryCount waits until the number of queries run on the tablet reach the specified count. func waitUntilQueryCount(t *testing.T, tab *sandboxconn.SandboxConn, count int) { + t.Helper() timeout := time.After(1 * time.Second) for { select { @@ -4428,7 +4410,7 @@ func TestStreamJoinQuery(t *testing.T) { func TestSysVarGlobalAndSession(t *testing.T) { executor, sbc1, _, _, _ := createExecutorEnv(t) executor.normalize = true - session := NewAutocommitSession(&vtgatepb.Session{EnableSystemSettings: true, SystemVariables: map[string]string{}}) + session := econtext.NewAutocommitSession(&vtgatepb.Session{EnableSystemSettings: true, SystemVariables: map[string]string{}}) sbc1.SetResults([]*sqltypes.Result{ sqltypes.MakeTestResult(sqltypes.MakeTestFields("innodb_lock_wait_timeout", "uint64"), "20"), diff --git a/go/vt/vtgate/executor_set_test.go b/go/vt/vtgate/executor_set_test.go index 12e8e272bd7..62101639a11 100644 --- a/go/vt/vtgate/executor_set_test.go +++ b/go/vt/vtgate/executor_set_test.go @@ -22,6 +22,7 @@ import ( "vitess.io/vitess/go/mysql/sqlerror" querypb "vitess.io/vitess/go/vt/proto/query" + econtext "vitess.io/vitess/go/vt/vtgate/executorcontext" "vitess.io/vitess/go/test/utils" @@ -266,7 +267,7 @@ func TestExecutorSet(t *testing.T) { }} for i, tcase := range testcases { t.Run(fmt.Sprintf("%d-%s", i, tcase.in), func(t *testing.T) { - session := NewSafeSession(&vtgatepb.Session{Autocommit: true}) + session := econtext.NewSafeSession(&vtgatepb.Session{Autocommit: true}) _, err := executorEnv.Execute(ctx, nil, "TestExecute", session, tcase.in, nil) if tcase.err == "" { require.NoError(t, err) @@ -374,7 +375,7 @@ func TestExecutorSetOp(t *testing.T) { }} for _, tcase := range testcases { t.Run(tcase.in, func(t *testing.T) { - session := NewAutocommitSession(&vtgatepb.Session{ + session := econtext.NewAutocommitSession(&vtgatepb.Session{ TargetString: "@primary", }) session.TargetString = KsTestUnsharded @@ -392,7 +393,7 @@ func TestExecutorSetMetadata(t *testing.T) { t.Run("Session 1", func(t *testing.T) { executor, _, _, _, ctx := createExecutorEnv(t) - session := NewSafeSession(&vtgatepb.Session{TargetString: "@primary", Autocommit: true}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: "@primary", Autocommit: true}) set := "set @@vitess_metadata.app_keyspace_v1= '1'" _, err := executor.Execute(ctx, nil, "TestExecute", session, set, nil) @@ -406,7 +407,7 @@ func TestExecutorSetMetadata(t *testing.T) { }() executor, _, _, _, ctx := createExecutorEnv(t) - session := NewSafeSession(&vtgatepb.Session{TargetString: "@primary", Autocommit: true}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: "@primary", Autocommit: true}) set := "set @@vitess_metadata.app_keyspace_v1= '1'" _, err := executor.Execute(ctx, nil, "TestExecute", session, set, nil) @@ -469,7 +470,7 @@ func TestPlanExecutorSetUDV(t *testing.T) { }} for _, tcase := range testcases { t.Run(tcase.in, func(t *testing.T) { - session := NewSafeSession(&vtgatepb.Session{Autocommit: true}) + session := econtext.NewSafeSession(&vtgatepb.Session{Autocommit: true}) _, err := executor.Execute(ctx, nil, "TestExecute", session, tcase.in, nil) if err != nil { require.EqualError(t, err, tcase.err) @@ -515,7 +516,7 @@ func TestSetVar(t *testing.T) { executor, _, _, sbc, ctx := createCustomExecutor(t, "{}", "8.0.0") executor.normalize = true - session := NewAutocommitSession(&vtgatepb.Session{EnableSystemSettings: true, TargetString: KsTestUnsharded}) + session := econtext.NewAutocommitSession(&vtgatepb.Session{EnableSystemSettings: true, TargetString: KsTestUnsharded}) sbc.SetResults([]*sqltypes.Result{sqltypes.MakeTestResult( sqltypes.MakeTestFields("orig|new", "varchar|varchar"), @@ -554,7 +555,7 @@ func TestSetVarShowVariables(t *testing.T) { executor, _, _, sbc, ctx := createCustomExecutor(t, "{}", "8.0.0") executor.normalize = true - session := NewAutocommitSession(&vtgatepb.Session{EnableSystemSettings: true, TargetString: KsTestUnsharded}) + session := econtext.NewAutocommitSession(&vtgatepb.Session{EnableSystemSettings: true, TargetString: KsTestUnsharded}) sbc.SetResults([]*sqltypes.Result{ // select query result for checking any change in system settings @@ -597,7 +598,7 @@ func TestExecutorSetAndSelect(t *testing.T) { sysVar: "tx_isolation", exp: `[[VARCHAR("READ-UNCOMMITTED")]]`, // this returns the value set in previous query. }} - session := NewAutocommitSession(&vtgatepb.Session{TargetString: KsTestUnsharded, EnableSystemSettings: true}) + session := econtext.NewAutocommitSession(&vtgatepb.Session{TargetString: KsTestUnsharded, EnableSystemSettings: true}) for _, tcase := range testcases { t.Run(fmt.Sprintf("%s-%s", tcase.sysVar, tcase.val), func(t *testing.T) { sbc.ExecCount.Store(0) // reset the value @@ -631,7 +632,7 @@ func TestExecutorSetAndSelect(t *testing.T) { func TestExecutorTimeZone(t *testing.T) { e, _, _, _, ctx := createExecutorEnv(t) - session := NewAutocommitSession(&vtgatepb.Session{TargetString: KsTestUnsharded, EnableSystemSettings: true}) + session := econtext.NewAutocommitSession(&vtgatepb.Session{TargetString: KsTestUnsharded, EnableSystemSettings: true}) session.SetSystemVariable("time_zone", "'+08:00'") qr, err := e.Execute(ctx, nil, "TestExecutorSetAndSelect", session, "select now()", nil) diff --git a/go/vt/vtgate/executor_stream_test.go b/go/vt/vtgate/executor_stream_test.go index b8cfeaf3cd5..a8500dd59c4 100644 --- a/go/vt/vtgate/executor_stream_test.go +++ b/go/vt/vtgate/executor_stream_test.go @@ -31,6 +31,7 @@ import ( topodatapb "vitess.io/vitess/go/vt/proto/topodata" vtgatepb "vitess.io/vitess/go/vt/proto/vtgate" "vitess.io/vitess/go/vt/vtenv" + econtext "vitess.io/vitess/go/vt/vtgate/executorcontext" "vitess.io/vitess/go/vt/vtgate/logstats" _ "vitess.io/vitess/go/vt/vtgate/vindexes" "vitess.io/vitess/go/vt/vttablet/sandboxconn" @@ -102,7 +103,7 @@ func executorStreamMessages(executor *Executor, sql string) (qr *sqltypes.Result ctx, nil, "TestExecuteStream", - NewSafeSession(session), + econtext.NewSafeSession(session), sql, nil, func(qr *sqltypes.Result) error { diff --git a/go/vt/vtgate/executor_test.go b/go/vt/vtgate/executor_test.go index 3732a37d1d1..2b6d4710bce 100644 --- a/go/vt/vtgate/executor_test.go +++ b/go/vt/vtgate/executor_test.go @@ -36,6 +36,8 @@ import ( "github.com/stretchr/testify/require" "google.golang.org/protobuf/proto" + econtext "vitess.io/vitess/go/vt/vtgate/executorcontext" + "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/mysql/sqlerror" "vitess.io/vitess/go/sqltypes" @@ -64,7 +66,7 @@ func TestExecutorResultsExceeded(t *testing.T) { warnMemoryRows = 3 defer func() { warnMemoryRows = save }() - session := NewSafeSession(&vtgatepb.Session{TargetString: "@primary"}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: "@primary"}) initial := warnings.Counts()["ResultsExceeded"] @@ -88,7 +90,7 @@ func TestExecutorMaxMemoryRowsExceeded(t *testing.T) { maxMemoryRows = 3 defer func() { maxMemoryRows = save }() - session := NewSafeSession(&vtgatepb.Session{TargetString: "@primary"}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: "@primary"}) result := sqltypes.MakeTestResult(sqltypes.MakeTestFields("col", "int64"), "1", "2", "3", "4") fn := func(r *sqltypes.Result) error { return nil @@ -122,7 +124,7 @@ func TestExecutorMaxMemoryRowsExceeded(t *testing.T) { func TestExecutorTransactionsNoAutoCommit(t *testing.T) { executor, _, _, sbclookup, ctx := createExecutorEnv(t) - session := NewSafeSession(&vtgatepb.Session{TargetString: "@primary", SessionUUID: "suuid"}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: "@primary", SessionUUID: "suuid"}) logChan := executor.queryLogger.Subscribe("Test") defer executor.queryLogger.Unsubscribe(logChan) @@ -188,7 +190,7 @@ func TestExecutorTransactionsNoAutoCommit(t *testing.T) { } // Prevent use of non-primary if in_transaction is on. - session = NewSafeSession(&vtgatepb.Session{TargetString: "@primary", InTransaction: true}) + session = econtext.NewSafeSession(&vtgatepb.Session{TargetString: "@primary", InTransaction: true}) _, err = executor.Execute(ctx, nil, "TestExecute", session, "use @replica", nil) require.EqualError(t, err, `can't execute the given command because you have an active transaction`) } @@ -205,7 +207,7 @@ func TestDirectTargetRewrites(t *testing.T) { } sql := "select database()" - _, err := executor.Execute(ctx, nil, "TestExecute", NewSafeSession(session), sql, map[string]*querypb.BindVariable{}) + _, err := executor.Execute(ctx, nil, "TestExecute", econtext.NewSafeSession(session), sql, map[string]*querypb.BindVariable{}) require.NoError(t, err) assertQueries(t, sbclookup, []*querypb.BoundQuery{{ Sql: "select :__vtdbname as `database()` from dual", @@ -216,7 +218,7 @@ func TestDirectTargetRewrites(t *testing.T) { func TestExecutorTransactionsAutoCommit(t *testing.T) { executor, _, _, sbclookup, ctx := createExecutorEnv(t) - session := NewSafeSession(&vtgatepb.Session{TargetString: "@primary", Autocommit: true, SessionUUID: "suuid"}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: "@primary", Autocommit: true, SessionUUID: "suuid"}) logChan := executor.queryLogger.Subscribe("Test") defer executor.queryLogger.Unsubscribe(logChan) @@ -270,7 +272,7 @@ func TestExecutorTransactionsAutoCommitStreaming(t *testing.T) { executor, _, _, sbclookup, ctx := createExecutorEnv(t) oltpOptions := &querypb.ExecuteOptions{Workload: querypb.ExecuteOptions_OLTP} - session := NewSafeSession(&vtgatepb.Session{ + session := econtext.NewSafeSession(&vtgatepb.Session{ TargetString: "@primary", Autocommit: true, Options: oltpOptions, @@ -339,7 +341,7 @@ func TestExecutorDeleteMetadata(t *testing.T) { }() executor, _, _, _, ctx := createExecutorEnv(t) - session := NewSafeSession(&vtgatepb.Session{TargetString: "@primary", Autocommit: true}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: "@primary", Autocommit: true}) set := "set @@vitess_metadata.app_v1= '1'" _, err := executor.Execute(ctx, nil, "TestExecute", session, set, nil) @@ -367,7 +369,7 @@ func TestExecutorDeleteMetadata(t *testing.T) { func TestExecutorAutocommit(t *testing.T) { executor, _, _, sbclookup, ctx := createExecutorEnv(t) - session := NewSafeSession(&vtgatepb.Session{TargetString: "@primary"}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: "@primary"}) logChan := executor.queryLogger.Subscribe("Test") defer executor.queryLogger.Unsubscribe(logChan) @@ -446,7 +448,7 @@ func TestExecutorAutocommit(t *testing.T) { // transition autocommit from 0 to 1 in the middle of a transaction. startCount = sbclookup.CommitCount.Load() - session = NewSafeSession(&vtgatepb.Session{TargetString: "@primary"}) + session = econtext.NewSafeSession(&vtgatepb.Session{TargetString: "@primary"}) _, err = executor.Execute(ctx, nil, "TestExecute", session, "begin", nil) require.NoError(t, err) _, err = executor.Execute(ctx, nil, "TestExecute", session, "update main1 set id=1", nil) @@ -468,7 +470,7 @@ func TestExecutorAutocommit(t *testing.T) { func TestExecutorShowColumns(t *testing.T) { executor, sbc1, sbc2, sbclookup, ctx := createExecutorEnv(t) - session := NewSafeSession(&vtgatepb.Session{TargetString: ""}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: ""}) queries := []string{ "SHOW COLUMNS FROM `user` in `TestExecutor`", @@ -520,7 +522,7 @@ func assertMatchesNoOrder(t *testing.T, expected, got string) { func TestExecutorShow(t *testing.T) { executor, _, _, sbclookup, ctx := createExecutorEnv(t) - session := NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}) for _, query := range []string{"show vitess_keyspaces", "show keyspaces"} { qr, err := executor.Execute(ctx, nil, "TestExecute", session, query, nil) @@ -545,7 +547,7 @@ func TestExecutorShow(t *testing.T) { _, err = executor.Execute(ctx, nil, "TestExecute", session, "use @primary", nil) require.NoError(t, err) _, err = executor.Execute(ctx, nil, "TestExecute", session, "show tables", nil) - assert.EqualError(t, err, errNoKeyspace.Error(), "'show tables' should fail without a keyspace") + assert.EqualError(t, err, econtext.ErrNoKeyspace.Error(), "'show tables' should fail without a keyspace") assert.Empty(t, sbclookup.Queries, "sbclookup unexpectedly has queries already") showResults := &sqltypes.Result{ @@ -920,7 +922,7 @@ func TestExecutorShow(t *testing.T) { query = "show vschema vindexes on user" _, err = executor.Execute(ctx, nil, "TestExecute", session, query, nil) - wantErr := errNoKeyspace.Error() + wantErr := econtext.ErrNoKeyspace.Error() assert.EqualError(t, err, wantErr, query) query = "show vschema vindexes on TestExecutor.garbage" @@ -1024,7 +1026,7 @@ func TestExecutorShow(t *testing.T) { utils.MustMatch(t, wantqr, qr, fmt.Sprintf("%s, with a bad keyspace", query)) query = "show vschema tables" - session = NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded}) + session = econtext.NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded}) qr, err = executor.Execute(ctx, nil, "TestExecute", session, query, nil) require.NoError(t, err) wantqr = &sqltypes.Result{ @@ -1050,9 +1052,9 @@ func TestExecutorShow(t *testing.T) { utils.MustMatch(t, wantqr, qr, query) query = "show vschema tables" - session = NewSafeSession(&vtgatepb.Session{}) + session = econtext.NewSafeSession(&vtgatepb.Session{}) _, err = executor.Execute(ctx, nil, "TestExecute", session, query, nil) - want = errNoKeyspace.Error() + want = econtext.ErrNoKeyspace.Error() assert.EqualError(t, err, want, query) query = "show 10" @@ -1061,7 +1063,7 @@ func TestExecutorShow(t *testing.T) { assert.EqualError(t, err, want, query) query = "show vschema tables" - session = NewSafeSession(&vtgatepb.Session{TargetString: "no_such_keyspace"}) + session = econtext.NewSafeSession(&vtgatepb.Session{TargetString: "no_such_keyspace"}) _, err = executor.Execute(ctx, nil, "TestExecute", session, query, nil) want = "VT05003: unknown database 'no_such_keyspace' in vschema" assert.EqualError(t, err, want, query) @@ -1080,7 +1082,7 @@ func TestExecutorShow(t *testing.T) { func TestExecutorShowTargeted(t *testing.T) { executor, _, sbc2, _, ctx := createExecutorEnv(t) - session := NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor/40-60"}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor/40-60"}) queries := []string{ "show databases", @@ -1107,7 +1109,7 @@ func TestExecutorShowTargeted(t *testing.T) { func TestExecutorShowFromSystemSchema(t *testing.T) { executor, _, _, _, ctx := createExecutorEnv(t) - session := NewSafeSession(&vtgatepb.Session{TargetString: "mysql"}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: "mysql"}) _, err := executor.Execute(ctx, nil, "TestExecutorShowFromSystemSchema", session, "show tables", nil) require.NoError(t, err) @@ -1116,7 +1118,7 @@ func TestExecutorShowFromSystemSchema(t *testing.T) { func TestExecutorUse(t *testing.T) { executor, _, _, _, ctx := createExecutorEnv(t) - session := NewSafeSession(&vtgatepb.Session{Autocommit: true, TargetString: "@primary"}) + session := econtext.NewSafeSession(&vtgatepb.Session{Autocommit: true, TargetString: "@primary"}) stmts := []string{ "use TestExecutor", @@ -1135,13 +1137,13 @@ func TestExecutorUse(t *testing.T) { utils.MustMatch(t, wantSession, session.Session, "session does not match") } - _, err := executor.Execute(ctx, nil, "TestExecute", NewSafeSession(&vtgatepb.Session{}), "use 1", nil) + _, err := executor.Execute(ctx, nil, "TestExecute", econtext.NewSafeSession(&vtgatepb.Session{}), "use 1", nil) wantErr := "syntax error at position 6 near '1'" if err == nil || err.Error() != wantErr { t.Errorf("got: %v, want %v", err, wantErr) } - _, err = executor.Execute(ctx, nil, "TestExecute", NewSafeSession(&vtgatepb.Session{}), "use UnexistentKeyspace", nil) + _, err = executor.Execute(ctx, nil, "TestExecute", econtext.NewSafeSession(&vtgatepb.Session{}), "use UnexistentKeyspace", nil) require.EqualError(t, err, "VT05003: unknown database 'UnexistentKeyspace' in vschema") } @@ -1155,7 +1157,7 @@ func TestExecutorComment(t *testing.T) { wantResult := &sqltypes.Result{} for _, stmt := range stmts { - gotResult, err := executor.Execute(ctx, nil, "TestExecute", NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded}), stmt, nil) + gotResult, err := executor.Execute(ctx, nil, "TestExecute", econtext.NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded}), stmt, nil) if err != nil { t.Error(err) } @@ -1240,9 +1242,9 @@ func TestExecutorDDL(t *testing.T) { sbc2.ExecCount.Store(0) sbclookup.ExecCount.Store(0) stmtType := "DDL" - _, err := executor.Execute(ctx, nil, "TestExecute", NewSafeSession(&vtgatepb.Session{TargetString: tc.targetStr}), stmt, nil) + _, err := executor.Execute(ctx, nil, "TestExecute", econtext.NewSafeSession(&vtgatepb.Session{TargetString: tc.targetStr}), stmt, nil) if tc.hasNoKeyspaceErr { - require.EqualError(t, err, errNoKeyspace.Error(), "expect query to fail: %q", stmt) + require.EqualError(t, err, econtext.ErrNoKeyspace.Error(), "expect query to fail: %q", stmt) stmtType = "" // For error case, plan is not generated to query log will not contain any stmtType. } else { require.NoError(t, err, "did not expect error for query: %q", stmt) @@ -1278,9 +1280,9 @@ func TestExecutorDDL(t *testing.T) { sbc1.ExecCount.Store(0) sbc2.ExecCount.Store(0) sbclookup.ExecCount.Store(0) - _, err := executor.Execute(ctx, nil, "TestExecute", NewSafeSession(&vtgatepb.Session{TargetString: ""}), stmt.input, nil) + _, err := executor.Execute(ctx, nil, "TestExecute", econtext.NewSafeSession(&vtgatepb.Session{TargetString: ""}), stmt.input, nil) if stmt.hasErr { - require.EqualError(t, err, errNoKeyspace.Error(), "expect query to fail") + require.EqualError(t, err, econtext.ErrNoKeyspace.Error(), "expect query to fail") testQueryLog(t, executor, logChan, "TestExecute", "", stmt.input, 0) } else { require.NoError(t, err) @@ -1297,13 +1299,13 @@ func TestExecutorDDLFk(t *testing.T) { } for _, stmt := range stmts { - for _, fkMode := range []string{"allow", "disallow"} { - t.Run(stmt+fkMode, func(t *testing.T) { + for _, mode := range []string{"allow", "disallow"} { + t.Run(stmt+mode, func(t *testing.T) { executor, _, _, sbc, ctx := createExecutorEnv(t) sbc.ExecCount.Store(0) - foreignKeyMode = fkMode - _, err := executor.Execute(ctx, nil, mName, NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded}), stmt, nil) - if fkMode == "allow" { + executor.vConfig.ForeignKeyMode = fkMode(mode) + _, err := executor.Execute(ctx, nil, mName, econtext.NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded}), stmt, nil) + if mode == "allow" { require.NoError(t, err) require.EqualValues(t, 1, sbc.ExecCount.Load()) } else { @@ -1322,7 +1324,7 @@ func TestExecutorAlterVSchemaKeyspace(t *testing.T) { }() executor, _, _, _, ctx := createExecutorEnv(t) - session := NewSafeSession(&vtgatepb.Session{TargetString: "@primary", Autocommit: true}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: "@primary", Autocommit: true}) vschemaUpdates := make(chan *vschemapb.SrvVSchema, 2) executor.serv.WatchSrvVSchema(ctx, executor.cell, func(vschema *vschemapb.SrvVSchema, err error) bool { @@ -1364,7 +1366,7 @@ func TestExecutorCreateVindexDDL(t *testing.T) { t.Fatalf("test_vindex should not exist in original vschema") } - session := NewSafeSession(&vtgatepb.Session{TargetString: ks}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: ks}) stmt := "alter vschema create vindex test_vindex using hash" _, err := executor.Execute(ctx, nil, "TestExecute", session, stmt, nil) require.NoError(t, err) @@ -1388,7 +1390,7 @@ func TestExecutorCreateVindexDDL(t *testing.T) { // Create a new vschema keyspace implicitly by creating a vindex with a different // target in the session // ksNew := "test_new_keyspace" - session = NewSafeSession(&vtgatepb.Session{TargetString: ks}) + session = econtext.NewSafeSession(&vtgatepb.Session{TargetString: ks}) stmt = "alter vschema create vindex test_vindex2 using hash" _, err = executor.Execute(ctx, nil, "TestExecute", session, stmt, nil) if err != nil { @@ -1439,7 +1441,7 @@ func TestExecutorAddDropVschemaTableDDL(t *testing.T) { vschemaTables = append(vschemaTables, t) } - session := NewSafeSession(&vtgatepb.Session{TargetString: ks}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: ks}) stmt := "alter vschema add table test_table" _, err := executor.Execute(ctx, nil, "TestExecute", session, stmt, nil) require.NoError(t, err) @@ -1451,7 +1453,7 @@ func TestExecutorAddDropVschemaTableDDL(t *testing.T) { _ = waitForVschemaTables(t, ks, append([]string{"test_table", "test_table2"}, vschemaTables...), executor) // Should fail adding a table on a sharded keyspace - session = NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}) + session = econtext.NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}) stmt = "alter vschema add table test_table" _, err = executor.Execute(ctx, nil, "TestExecute", session, stmt, nil) require.EqualError(t, err, "add vschema table: unsupported on sharded keyspace TestExecutor") @@ -1470,7 +1472,7 @@ func TestExecutorVindexDDLACL(t *testing.T) { executor, _, _, _, ctx := createExecutorEnv(t) ks := "TestExecutor" - session := NewSafeSession(&vtgatepb.Session{TargetString: ks}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: ks}) ctxRedUser := callerid.NewContext(ctx, &vtrpcpb.CallerID{}, &querypb.VTGateCallerID{Username: "redUser"}) ctxBlueUser := callerid.NewContext(ctx, &vtrpcpb.CallerID{}, &querypb.VTGateCallerID{Username: "blueUser"}) @@ -1515,7 +1517,7 @@ func TestExecutorVindexDDLACL(t *testing.T) { func TestExecutorUnrecognized(t *testing.T) { executor, _, _, _, ctx := createExecutorEnv(t) - _, err := executor.Execute(ctx, nil, "TestExecute", NewSafeSession(&vtgatepb.Session{}), "invalid statement", nil) + _, err := executor.Execute(ctx, nil, "TestExecute", econtext.NewSafeSession(&vtgatepb.Session{}), "invalid statement", nil) require.Error(t, err, "unrecognized statement: invalid statement'") } @@ -1525,7 +1527,7 @@ func TestExecutorDeniedErrorNoBuffer(t *testing.T) { vschemaWaitTimeout = 500 * time.Millisecond - session := NewAutocommitSession(&vtgatepb.Session{TargetString: "@primary"}) + session := econtext.NewAutocommitSession(&vtgatepb.Session{TargetString: "@primary"}) startExec := time.Now() _, err := executor.Execute(ctx, nil, "TestExecutorDeniedErrorNoBuffer", session, "select * from user", nil) require.NoError(t, err, "enforce denied tables not buffered") @@ -1559,9 +1561,8 @@ var pv = querypb.ExecuteOptions_Gen4 func TestGetPlanUnnormalized(t *testing.T) { r, _, _, _, ctx := createExecutorEnv(t) - - emptyvc, _ := newVCursorImpl(NewSafeSession(&vtgatepb.Session{TargetString: "@unknown"}), makeComments(""), r, nil, r.vm, r.VSchema(), r.resolver.resolver, nil, false, pv) - unshardedvc, _ := newVCursorImpl(NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded + "@unknown"}), makeComments(""), r, nil, r.vm, r.VSchema(), r.resolver.resolver, nil, false, pv) + emptyvc, _ := econtext.NewVCursorImpl(econtext.NewSafeSession(&vtgatepb.Session{TargetString: "@unknown"}), makeComments(""), r, nil, r.vm, r.VSchema(), r.resolver.resolver, nil, nullResultsObserver{}, econtext.VCursorConfig{}) + unshardedvc, _ := econtext.NewVCursorImpl(econtext.NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded + "@unknown"}), makeComments(""), r, nil, r.vm, r.VSchema(), r.resolver.resolver, nil, nullResultsObserver{}, econtext.VCursorConfig{}) query1 := "select * from music_user_map where id = 1" plan1, logStats1 := getPlanCached(t, ctx, r, emptyvc, query1, makeComments(" /* comment */"), map[string]*querypb.BindVariable{}, false) @@ -1604,7 +1605,7 @@ func assertCacheSize(t *testing.T, c *PlanCache, expected int) { } } -func assertCacheContains(t *testing.T, e *Executor, vc *vcursorImpl, sql string) *engine.Plan { +func assertCacheContains(t *testing.T, e *Executor, vc *econtext.VCursorImpl, sql string) *engine.Plan { t.Helper() var plan *engine.Plan @@ -1623,9 +1624,9 @@ func assertCacheContains(t *testing.T, e *Executor, vc *vcursorImpl, sql string) return plan } -func getPlanCached(t *testing.T, ctx context.Context, e *Executor, vcursor *vcursorImpl, sql string, comments sqlparser.MarginComments, bindVars map[string]*querypb.BindVariable, skipQueryPlanCache bool) (*engine.Plan, *logstats.LogStats) { +func getPlanCached(t *testing.T, ctx context.Context, e *Executor, vcursor *econtext.VCursorImpl, sql string, comments sqlparser.MarginComments, bindVars map[string]*querypb.BindVariable, skipQueryPlanCache bool) (*engine.Plan, *logstats.LogStats) { logStats := logstats.NewLogStats(ctx, "Test", "", "", nil) - vcursor.safeSession = &SafeSession{ + vcursor.SafeSession = &econtext.SafeSession{ Session: &vtgatepb.Session{ Options: &querypb.ExecuteOptions{SkipQueryPlanCache: skipQueryPlanCache}}, } @@ -1644,7 +1645,7 @@ func TestGetPlanCacheUnnormalized(t *testing.T) { t.Run("Cache", func(t *testing.T) { r, _, _, _, ctx := createExecutorEnv(t) - emptyvc, _ := newVCursorImpl(NewSafeSession(&vtgatepb.Session{TargetString: "@unknown"}), makeComments(""), r, nil, r.vm, r.VSchema(), r.resolver.resolver, nil, false, pv) + emptyvc, _ := econtext.NewVCursorImpl(econtext.NewSafeSession(&vtgatepb.Session{TargetString: "@unknown"}), makeComments(""), r, nil, r.vm, r.VSchema(), r.resolver.resolver, nil, nullResultsObserver{}, econtext.VCursorConfig{}) query1 := "select * from music_user_map where id = 1" _, logStats1 := getPlanCached(t, ctx, r, emptyvc, query1, makeComments(" /* comment */"), map[string]*querypb.BindVariable{}, true) @@ -1668,7 +1669,7 @@ func TestGetPlanCacheUnnormalized(t *testing.T) { // Skip cache using directive r, _, _, _, ctx := createExecutorEnv(t) - unshardedvc, _ := newVCursorImpl(NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded + "@unknown"}), makeComments(""), r, nil, r.vm, r.VSchema(), r.resolver.resolver, nil, false, pv) + unshardedvc, _ := econtext.NewVCursorImpl(econtext.NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded + "@unknown"}), makeComments(""), r, nil, r.vm, r.VSchema(), r.resolver.resolver, nil, nullResultsObserver{}, r.vConfig) query1 := "insert /*vt+ SKIP_QUERY_PLAN_CACHE=1 */ into user(id) values (1), (2)" getPlanCached(t, ctx, r, unshardedvc, query1, makeComments(" /* comment */"), map[string]*querypb.BindVariable{}, false) @@ -1679,12 +1680,12 @@ func TestGetPlanCacheUnnormalized(t *testing.T) { assertCacheSize(t, r.plans, 1) // the target string will be resolved and become part of the plan cache key, which adds a new entry - ksIDVc1, _ := newVCursorImpl(NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded + "[deadbeef]"}), makeComments(""), r, nil, r.vm, r.VSchema(), r.resolver.resolver, nil, false, pv) + ksIDVc1, _ := econtext.NewVCursorImpl(econtext.NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded + "[deadbeef]"}), makeComments(""), r, nil, r.vm, r.VSchema(), r.resolver.resolver, nil, nullResultsObserver{}, r.vConfig) getPlanCached(t, ctx, r, ksIDVc1, query1, makeComments(" /* comment */"), map[string]*querypb.BindVariable{}, false) assertCacheSize(t, r.plans, 2) // the target string will be resolved and become part of the plan cache key, as it's an unsharded ks, it will be the same entry as above - ksIDVc2, _ := newVCursorImpl(NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded + "[beefdead]"}), makeComments(""), r, nil, r.vm, r.VSchema(), r.resolver.resolver, nil, false, pv) + ksIDVc2, _ := econtext.NewVCursorImpl(econtext.NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded + "[beefdead]"}), makeComments(""), r, nil, r.vm, r.VSchema(), r.resolver.resolver, nil, nullResultsObserver{}, r.vConfig) getPlanCached(t, ctx, r, ksIDVc2, query1, makeComments(" /* comment */"), map[string]*querypb.BindVariable{}, false) assertCacheSize(t, r.plans, 2) }) @@ -1694,7 +1695,7 @@ func TestGetPlanCacheNormalized(t *testing.T) { t.Run("Cache", func(t *testing.T) { r, _, _, _, ctx := createExecutorEnv(t) r.normalize = true - emptyvc, _ := newVCursorImpl(NewSafeSession(&vtgatepb.Session{TargetString: "@unknown"}), makeComments(""), r, nil, r.vm, r.VSchema(), r.resolver.resolver, nil, false, pv) + emptyvc, _ := econtext.NewVCursorImpl(econtext.NewSafeSession(&vtgatepb.Session{TargetString: "@unknown"}), makeComments(""), r, nil, r.vm, r.VSchema(), r.resolver.resolver, nil, nullResultsObserver{}, r.vConfig) query1 := "select * from music_user_map where id = 1" _, logStats1 := getPlanCached(t, ctx, r, emptyvc, query1, makeComments(" /* comment */"), map[string]*querypb.BindVariable{}, true /* skipQueryPlanCache */) @@ -1711,7 +1712,7 @@ func TestGetPlanCacheNormalized(t *testing.T) { // Skip cache using directive r, _, _, _, ctx := createExecutorEnv(t) r.normalize = true - unshardedvc, _ := newVCursorImpl(NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded + "@unknown"}), makeComments(""), r, nil, r.vm, r.VSchema(), r.resolver.resolver, nil, false, pv) + unshardedvc, _ := econtext.NewVCursorImpl(econtext.NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded + "@unknown"}), makeComments(""), r, nil, r.vm, r.VSchema(), r.resolver.resolver, nil, nullResultsObserver{}, r.vConfig) query1 := "insert /*vt+ SKIP_QUERY_PLAN_CACHE=1 */ into user(id) values (1), (2)" getPlanCached(t, ctx, r, unshardedvc, query1, makeComments(" /* comment */"), map[string]*querypb.BindVariable{}, false) @@ -1722,12 +1723,12 @@ func TestGetPlanCacheNormalized(t *testing.T) { assertCacheSize(t, r.plans, 1) // the target string will be resolved and become part of the plan cache key, which adds a new entry - ksIDVc1, _ := newVCursorImpl(NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded + "[deadbeef]"}), makeComments(""), r, nil, r.vm, r.VSchema(), r.resolver.resolver, nil, false, pv) + ksIDVc1, _ := econtext.NewVCursorImpl(econtext.NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded + "[deadbeef]"}), makeComments(""), r, nil, r.vm, r.VSchema(), r.resolver.resolver, nil, nullResultsObserver{}, r.vConfig) getPlanCached(t, ctx, r, ksIDVc1, query1, makeComments(" /* comment */"), map[string]*querypb.BindVariable{}, false) assertCacheSize(t, r.plans, 2) // the target string will be resolved and become part of the plan cache key, as it's an unsharded ks, it will be the same entry as above - ksIDVc2, _ := newVCursorImpl(NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded + "[beefdead]"}), makeComments(""), r, nil, r.vm, r.VSchema(), r.resolver.resolver, nil, false, pv) + ksIDVc2, _ := econtext.NewVCursorImpl(econtext.NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded + "[beefdead]"}), makeComments(""), r, nil, r.vm, r.VSchema(), r.resolver.resolver, nil, nullResultsObserver{}, r.vConfig) getPlanCached(t, ctx, r, ksIDVc2, query1, makeComments(" /* comment */"), map[string]*querypb.BindVariable{}, false) assertCacheSize(t, r.plans, 2) }) @@ -1737,8 +1738,8 @@ func TestGetPlanNormalized(t *testing.T) { r, _, _, _, ctx := createExecutorEnv(t) r.normalize = true - emptyvc, _ := newVCursorImpl(NewSafeSession(&vtgatepb.Session{TargetString: "@unknown"}), makeComments(""), r, nil, r.vm, r.VSchema(), r.resolver.resolver, nil, false, pv) - unshardedvc, _ := newVCursorImpl(NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded + "@unknown"}), makeComments(""), r, nil, r.vm, r.VSchema(), r.resolver.resolver, nil, false, pv) + emptyvc, _ := econtext.NewVCursorImpl(econtext.NewSafeSession(&vtgatepb.Session{TargetString: "@unknown"}), makeComments(""), r, nil, r.vm, r.VSchema(), r.resolver.resolver, nil, nullResultsObserver{}, econtext.VCursorConfig{}) + unshardedvc, _ := econtext.NewVCursorImpl(econtext.NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded + "@unknown"}), makeComments(""), r, nil, r.vm, r.VSchema(), r.resolver.resolver, nil, nullResultsObserver{}, econtext.VCursorConfig{}) query1 := "select * from music_user_map where id = 1" query2 := "select * from music_user_map where id = 2" @@ -1785,7 +1786,7 @@ func TestGetPlanPriority(t *testing.T) { {name: "empty priority", sql: "select * from music_user_map", expectedPriority: "", expectedError: nil}, } - session := NewSafeSession(&vtgatepb.Session{TargetString: "@unknown", Options: &querypb.ExecuteOptions{}}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: "@unknown", Options: &querypb.ExecuteOptions{}}) for _, aTestCase := range testCases { testCase := aTestCase @@ -1795,7 +1796,7 @@ func TestGetPlanPriority(t *testing.T) { r.normalize = true logStats := logstats.NewLogStats(ctx, "Test", "", "", nil) - vCursor, err := newVCursorImpl(session, makeComments(""), r, nil, r.vm, r.VSchema(), r.resolver.resolver, nil, false, pv) + vCursor, err := econtext.NewVCursorImpl(session, makeComments(""), r, nil, r.vm, r.VSchema(), r.resolver.resolver, nil, nullResultsObserver{}, econtext.VCursorConfig{}) assert.NoError(t, err) stmt, err := sqlparser.NewTestParser().Parse(testCase.sql) @@ -1809,7 +1810,7 @@ func TestGetPlanPriority(t *testing.T) { } else { assert.NoError(t, err) assert.Equal(t, testCase.expectedPriority, priorityFromStatement) - assert.Equal(t, testCase.expectedPriority, vCursor.safeSession.Options.Priority) + assert.Equal(t, testCase.expectedPriority, vCursor.SafeSession.Options.Priority) } }) } @@ -1966,7 +1967,7 @@ func TestExecutorMaxPayloadSizeExceeded(t *testing.T) { executor, _, _, _, _ := createExecutorEnv(t) - session := NewSafeSession(&vtgatepb.Session{TargetString: "@primary"}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: "@primary"}) warningCount := warnings.Counts()["WarnPayloadSizeExceeded"] testMaxPayloadSizeExceeded := []string{ "select * from main1", @@ -2014,7 +2015,7 @@ func TestOlapSelectDatabase(t *testing.T) { cbInvoked = true return nil } - err := executor.StreamExecute(context.Background(), nil, "TestExecute", NewSafeSession(session), sql, nil, cb) + err := executor.StreamExecute(context.Background(), nil, "TestExecute", econtext.NewSafeSession(session), sql, nil, cb) assert.NoError(t, err) assert.True(t, cbInvoked) } @@ -2022,7 +2023,7 @@ func TestOlapSelectDatabase(t *testing.T) { func TestExecutorClearsWarnings(t *testing.T) { executor, _, _, _, _ := createExecutorEnv(t) - session := NewSafeSession(&vtgatepb.Session{ + session := econtext.NewSafeSession(&vtgatepb.Session{ Warnings: []*querypb.QueryWarning{{Code: 234, Message: "oh noes"}}, }) _, err := executor.Execute(context.Background(), nil, "TestExecute", session, "select 42", nil) @@ -2039,7 +2040,6 @@ func TestServingKeyspaces(t *testing.T) { executor, sbc1, _, sbclookup, ctx := createExecutorEnv(t) - executor.pv = querypb.ExecuteOptions_Gen4 gw, ok := executor.resolver.resolver.GetGateway().(*TabletGateway) require.True(t, ok) hc := gw.hc.(*discovery.FakeHealthCheck) @@ -2058,7 +2058,7 @@ func TestServingKeyspaces(t *testing.T) { }) require.ElementsMatch(t, []string{"TestExecutor", "TestUnsharded"}, gw.GetServingKeyspaces()) - result, err := executor.Execute(ctx, nil, "TestServingKeyspaces", NewSafeSession(&vtgatepb.Session{}), "select keyspace_name from dual", nil) + result, err := executor.Execute(ctx, nil, "TestServingKeyspaces", econtext.NewSafeSession(&vtgatepb.Session{}), "select keyspace_name from dual", nil) require.NoError(t, err) require.Equal(t, `[[VARCHAR("TestExecutor")]]`, fmt.Sprintf("%v", result.Rows)) @@ -2074,7 +2074,7 @@ func TestServingKeyspaces(t *testing.T) { // Clear plan cache, to force re-planning of the query. executor.ClearPlans() require.ElementsMatch(t, []string{"TestUnsharded"}, gw.GetServingKeyspaces()) - result, err = executor.Execute(ctx, nil, "TestServingKeyspaces", NewSafeSession(&vtgatepb.Session{}), "select keyspace_name from dual", nil) + result, err = executor.Execute(ctx, nil, "TestServingKeyspaces", econtext.NewSafeSession(&vtgatepb.Session{}), "select keyspace_name from dual", nil) require.NoError(t, err) require.Equal(t, `[[VARCHAR("TestUnsharded")]]`, fmt.Sprintf("%v", result.Rows)) } @@ -2150,9 +2150,9 @@ func TestExecutorOther(t *testing.T) { sbc2.ExecCount.Store(0) sbclookup.ExecCount.Store(0) - _, err := executor.Execute(ctx, nil, "TestExecute", NewSafeSession(&vtgatepb.Session{TargetString: tc.targetStr}), stmt, nil) + _, err := executor.Execute(ctx, nil, "TestExecute", econtext.NewSafeSession(&vtgatepb.Session{TargetString: tc.targetStr}), stmt, nil) if tc.hasNoKeyspaceErr { - assert.Error(t, err, errNoKeyspace) + assert.Error(t, err, econtext.ErrNoKeyspace.Error()) } else if tc.hasDestinationShardErr { assert.Errorf(t, err, "Destination can only be a single shard for statement: %s", stmt) } else { @@ -2206,7 +2206,7 @@ func TestExecutorAnalyze(t *testing.T) { sbc2.ExecCount.Store(0) sbclookup.ExecCount.Store(0) - _, err := executor.Execute(context.Background(), nil, "TestExecute", NewSafeSession(&vtgatepb.Session{TargetString: tc.targetStr}), stmt, nil) + _, err := executor.Execute(context.Background(), nil, "TestExecute", econtext.NewSafeSession(&vtgatepb.Session{TargetString: tc.targetStr}), stmt, nil) require.NoError(t, err) utils.MustMatch(t, tc.wantCnts, cnts{ @@ -2270,7 +2270,7 @@ func TestExecutorExplainStmt(t *testing.T) { sbc2.ExecCount.Store(0) sbclookup.ExecCount.Store(0) - _, err := executor.Execute(ctx, nil, "TestExecute", NewSafeSession(&vtgatepb.Session{TargetString: tc.targetStr}), stmt, nil) + _, err := executor.Execute(ctx, nil, "TestExecute", econtext.NewSafeSession(&vtgatepb.Session{TargetString: tc.targetStr}), stmt, nil) assert.NoError(t, err) utils.MustMatch(t, tc.wantCnts, cnts{ @@ -2360,9 +2360,9 @@ func TestExecutorOtherAdmin(t *testing.T) { sbc2.ExecCount.Store(0) sbclookup.ExecCount.Store(0) - _, err := executor.Execute(context.Background(), nil, "TestExecute", NewSafeSession(&vtgatepb.Session{TargetString: tc.targetStr}), stmt, nil) + _, err := executor.Execute(context.Background(), nil, "TestExecute", econtext.NewSafeSession(&vtgatepb.Session{TargetString: tc.targetStr}), stmt, nil) if tc.hasNoKeyspaceErr { - assert.Error(t, err, errNoKeyspace) + assert.Error(t, err, econtext.ErrNoKeyspace.Error()) } else if tc.hasDestinationShardErr { assert.Errorf(t, err, "Destination can only be a single shard for statement: %s, got: DestinationExactKeyRange(-)", stmt) } else { @@ -2387,7 +2387,7 @@ func TestExecutorSavepointInTx(t *testing.T) { logChan := executor.queryLogger.Subscribe("TestExecutorSavepoint") defer executor.queryLogger.Unsubscribe(logChan) - session := NewSafeSession(&vtgatepb.Session{Autocommit: false, TargetString: "@primary"}) + session := econtext.NewSafeSession(&vtgatepb.Session{Autocommit: false, TargetString: "@primary"}) _, err := exec(executor, session, "savepoint a") require.NoError(t, err) _, err = exec(executor, session, "rollback to a") @@ -2470,7 +2470,7 @@ func TestExecutorSavepointInTxWithReservedConn(t *testing.T) { logChan := executor.queryLogger.Subscribe("TestExecutorSavepoint") defer executor.queryLogger.Unsubscribe(logChan) - session := NewSafeSession(&vtgatepb.Session{Autocommit: true, TargetString: "TestExecutor", EnableSystemSettings: true}) + session := econtext.NewSafeSession(&vtgatepb.Session{Autocommit: true, TargetString: "TestExecutor", EnableSystemSettings: true}) sbc1.SetResults([]*sqltypes.Result{ sqltypes.MakeTestResult(sqltypes.MakeTestFields("orig|new", "varchar|varchar"), "a|"), }) @@ -2537,7 +2537,7 @@ func TestExecutorSavepointWithoutTx(t *testing.T) { logChan := executor.queryLogger.Subscribe("TestExecutorSavepoint") defer executor.queryLogger.Unsubscribe(logChan) - session := NewSafeSession(&vtgatepb.Session{Autocommit: true, TargetString: "@primary", InTransaction: false}) + session := econtext.NewSafeSession(&vtgatepb.Session{Autocommit: true, TargetString: "@primary", InTransaction: false}) _, err := exec(executor, session, "savepoint a") require.NoError(t, err) _, err = exec(executor, session, "rollback to a") @@ -2622,9 +2622,9 @@ func TestExecutorCallProc(t *testing.T) { sbc2.ExecCount.Store(0) sbcUnsharded.ExecCount.Store(0) - _, err := executor.Execute(context.Background(), nil, "TestExecute", NewSafeSession(&vtgatepb.Session{TargetString: tc.targetStr}), "CALL proc()", nil) + _, err := executor.Execute(context.Background(), nil, "TestExecute", econtext.NewSafeSession(&vtgatepb.Session{TargetString: tc.targetStr}), "CALL proc()", nil) if tc.hasNoKeyspaceErr { - assert.EqualError(t, err, errNoKeyspace.Error()) + assert.EqualError(t, err, econtext.ErrNoKeyspace.Error()) } else if tc.unshardedOnlyErr { require.EqualError(t, err, "CALL is not supported for sharded keyspace") } else { @@ -2644,9 +2644,9 @@ func TestExecutorTempTable(t *testing.T) { executor, _, _, sbcUnsharded, ctx := createExecutorEnv(t) initialWarningsCount := warnings.Counts()["WarnUnshardedOnly"] - executor.warnShardedOnly = true + executor.vConfig.WarnShardedOnly = true creatQuery := "create temporary table temp_t(id bigint primary key)" - session := NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded}) _, err := executor.Execute(ctx, nil, "TestExecutorTempTable", session, creatQuery, nil) require.NoError(t, err) assert.EqualValues(t, 1, sbcUnsharded.ExecCount.Load()) @@ -2665,7 +2665,7 @@ func TestExecutorShowVitessMigrations(t *testing.T) { executor, sbc1, sbc2, _, ctx := createExecutorEnv(t) showQuery := "show vitess_migrations" - session := NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}) _, err := executor.Execute(ctx, nil, "", session, showQuery, nil) require.NoError(t, err) assert.Contains(t, sbc1.StringQueries(), "show vitess_migrations") @@ -2676,7 +2676,7 @@ func TestExecutorDescHash(t *testing.T) { executor, _, _, _, ctx := createExecutorEnv(t) showQuery := "desc hash_index" - session := NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}) _, err := executor.Execute(ctx, nil, "", session, showQuery, nil) require.NoError(t, err) } @@ -2684,7 +2684,7 @@ func TestExecutorDescHash(t *testing.T) { func TestExecutorVExplainQueries(t *testing.T) { executor, _, _, sbclookup, ctx := createExecutorEnv(t) - session := NewAutocommitSession(&vtgatepb.Session{}) + session := econtext.NewAutocommitSession(&vtgatepb.Session{}) sbclookup.SetResults([]*sqltypes.Result{ sqltypes.MakeTestResult(sqltypes.MakeTestFields("name|user_id", "varchar|int64"), "apa|1", "apa|2"), @@ -2697,7 +2697,7 @@ func TestExecutorVExplainQueries(t *testing.T) { // Test the streaming side as well var results []sqltypes.Row - session = NewAutocommitSession(&vtgatepb.Session{}) + session = econtext.NewAutocommitSession(&vtgatepb.Session{}) err = executor.StreamExecute(ctx, nil, "TestExecutorVExplainQueries", session, "vexplain queries select * from user where name = 'apa'", nil, func(result *sqltypes.Result) error { results = append(results, result.Rows...) return nil @@ -2710,7 +2710,7 @@ func TestExecutorVExplainQueries(t *testing.T) { func TestExecutorStartTxnStmt(t *testing.T) { executor, _, _, _, ctx := createExecutorEnv(t) - session := NewAutocommitSession(&vtgatepb.Session{}) + session := econtext.NewAutocommitSession(&vtgatepb.Session{}) tcases := []struct { beginSQL string @@ -2757,7 +2757,7 @@ func TestExecutorPrepareExecute(t *testing.T) { executor, _, _, _, _ := createExecutorEnv(t) executor.normalize = true - session := NewAutocommitSession(&vtgatepb.Session{}) + session := econtext.NewAutocommitSession(&vtgatepb.Session{}) // prepare statement. _, err := executor.Execute(context.Background(), nil, "TestExecutorPrepareExecute", session, "prepare prep_user from 'select * from user where id = ?'", nil) @@ -2834,7 +2834,7 @@ func TestExecutorSettingsInTwoPC(t *testing.T) { sbc2.SetResults(tcase.testRes) // create a new session - session := NewSafeSession(&vtgatepb.Session{ + session := econtext.NewSafeSession(&vtgatepb.Session{ TargetString: KsTestSharded, TransactionMode: vtgatepb.TransactionMode_TWOPC, EnableSystemSettings: true, @@ -2892,7 +2892,7 @@ func TestExecutorRejectTwoPC(t *testing.T) { sbc2.SetResults(tcase.testRes) // create a new session - session := NewSafeSession(&vtgatepb.Session{ + session := econtext.NewSafeSession(&vtgatepb.Session{ TargetString: KsTestSharded, TransactionMode: vtgatepb.TransactionMode_TWOPC, EnableSystemSettings: true, @@ -2922,7 +2922,7 @@ func TestExecutorTruncateErrors(t *testing.T) { truncateErrorLen = 32 defer func() { truncateErrorLen = save }() - session := NewSafeSession(&vtgatepb.Session{}) + session := econtext.NewSafeSession(&vtgatepb.Session{}) fn := func(r *sqltypes.Result) error { return nil } @@ -2982,7 +2982,7 @@ func TestExecutorFlushStmt(t *testing.T) { for _, tc := range tcs { t.Run(tc.query+tc.targetStr, func(t *testing.T) { - _, err := executor.Execute(context.Background(), nil, "TestExecutorFlushStmt", NewSafeSession(&vtgatepb.Session{TargetString: tc.targetStr}), tc.query, nil) + _, err := executor.Execute(context.Background(), nil, "TestExecutorFlushStmt", econtext.NewSafeSession(&vtgatepb.Session{TargetString: tc.targetStr}), tc.query, nil) if tc.expectedErr == "" { require.NoError(t, err) } else { @@ -3029,7 +3029,7 @@ func TestExecutorKillStmt(t *testing.T) { allowKillStmt = !tc.disallow t.Run("execute:"+tc.query+tc.errStr, func(t *testing.T) { mysqlCtx := &fakeMysqlConnection{ErrMsg: tc.errStr} - _, err := executor.Execute(context.Background(), mysqlCtx, "TestExecutorKillStmt", NewAutocommitSession(&vtgatepb.Session{}), tc.query, nil) + _, err := executor.Execute(context.Background(), mysqlCtx, "TestExecutorKillStmt", econtext.NewAutocommitSession(&vtgatepb.Session{}), tc.query, nil) if tc.errStr != "" { require.ErrorContains(t, err, tc.errStr) } else { @@ -3039,7 +3039,7 @@ func TestExecutorKillStmt(t *testing.T) { }) t.Run("stream:"+tc.query+tc.errStr, func(t *testing.T) { mysqlCtx := &fakeMysqlConnection{ErrMsg: tc.errStr} - err := executor.StreamExecute(context.Background(), mysqlCtx, "TestExecutorKillStmt", NewAutocommitSession(&vtgatepb.Session{}), tc.query, nil, func(result *sqltypes.Result) error { + err := executor.StreamExecute(context.Background(), mysqlCtx, "TestExecutorKillStmt", econtext.NewAutocommitSession(&vtgatepb.Session{}), tc.query, nil, func(result *sqltypes.Result) error { return nil }) if tc.errStr != "" { @@ -3075,7 +3075,7 @@ func (f *fakeMysqlConnection) KillConnection(ctx context.Context, connID uint32) var _ vtgateservice.MySQLConnection = (*fakeMysqlConnection)(nil) -func exec(executor *Executor, session *SafeSession, sql string) (*sqltypes.Result, error) { +func exec(executor *Executor, session *econtext.SafeSession, sql string) (*sqltypes.Result, error) { return executor.Execute(context.Background(), nil, "TestExecute", session, sql, nil) } diff --git a/go/vt/vtgate/executor_vexplain_test.go b/go/vt/vtgate/executor_vexplain_test.go index 99eb77c7ed4..a9516492f1b 100644 --- a/go/vt/vtgate/executor_vexplain_test.go +++ b/go/vt/vtgate/executor_vexplain_test.go @@ -26,6 +26,8 @@ import ( "github.com/stretchr/testify/assert" + econtext "vitess.io/vitess/go/vt/vtgate/executorcontext" + "github.com/stretchr/testify/require" "vitess.io/vitess/go/sqltypes" @@ -135,7 +137,7 @@ func TestVExplainKeys(t *testing.T) { for _, tt := range tests { t.Run(tt.Query, func(t *testing.T) { executor, _, _, _, _ := createExecutorEnv(t) - session := NewSafeSession(&vtgatepb.Session{TargetString: "@primary"}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: "@primary"}) gotResult, err := executor.Execute(context.Background(), nil, "Execute", session, "vexplain keys "+tt.Query, nil) require.NoError(t, err) diff --git a/go/vt/vtgate/executor_vschema_ddl_test.go b/go/vt/vtgate/executor_vschema_ddl_test.go index 1c912ed0d62..825b65ab8f3 100644 --- a/go/vt/vtgate/executor_vschema_ddl_test.go +++ b/go/vt/vtgate/executor_vschema_ddl_test.go @@ -25,6 +25,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + econtext "vitess.io/vitess/go/vt/vtgate/executorcontext" + "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/test/utils" "vitess.io/vitess/go/vt/callerid" @@ -138,7 +140,7 @@ func TestPlanExecutorAlterVSchemaKeyspace(t *testing.T) { vschemaacl.AuthorizedDDLUsers = "" }() executor, _, _, _, ctx := createExecutorEnv(t) - session := NewSafeSession(&vtgatepb.Session{TargetString: "@primary", Autocommit: true}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: "@primary", Autocommit: true}) vschemaUpdates := make(chan *vschemapb.SrvVSchema, 2) executor.serv.WatchSrvVSchema(ctx, "aa", func(vschema *vschemapb.SrvVSchema, err error) bool { @@ -180,7 +182,7 @@ func TestPlanExecutorCreateVindexDDL(t *testing.T) { t.Fatalf("test_vindex should not exist in original vschema") } - session := NewSafeSession(&vtgatepb.Session{TargetString: ks}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: ks}) stmt := "alter vschema create vindex test_vindex using hash" _, err := executor.Execute(ctx, nil, "TestExecute", session, stmt, nil) require.NoError(t, err) @@ -222,7 +224,7 @@ func TestPlanExecutorDropVindexDDL(t *testing.T) { t.Fatalf("test_vindex should not exist in original vschema") } - session := NewSafeSession(&vtgatepb.Session{TargetString: ks}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: ks}) stmt := "alter vschema drop vindex test_vindex" _, err := executor.Execute(ctx, nil, "TestExecute", session, stmt, nil) wantErr := "vindex test_vindex does not exists in keyspace TestExecutor" @@ -296,7 +298,7 @@ func TestPlanExecutorAddDropVschemaTableDDL(t *testing.T) { vschemaTables = append(vschemaTables, t) } - session := NewSafeSession(&vtgatepb.Session{TargetString: ks}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: ks}) stmt := "alter vschema add table test_table" _, err := executor.Execute(ctx, nil, "TestExecute", session, stmt, nil) require.NoError(t, err) @@ -308,7 +310,7 @@ func TestPlanExecutorAddDropVschemaTableDDL(t *testing.T) { _ = waitForVschemaTables(t, ks, append([]string{"test_table", "test_table2"}, vschemaTables...), executor) // Should fail adding a table on a sharded keyspace - session = NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}) + session = econtext.NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}) stmt = "alter vschema add table test_table" _, err = executor.Execute(ctx, nil, "TestExecute", session, stmt, nil) wantErr := "add vschema table: unsupported on sharded keyspace TestExecutor" @@ -343,7 +345,7 @@ func TestExecutorAddSequenceDDL(t *testing.T) { vschemaTables = append(vschemaTables, t) } - session := NewSafeSession(&vtgatepb.Session{TargetString: ks}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: ks}) stmt := "alter vschema add sequence test_seq" _, err := executor.Execute(ctx, nil, "TestExecute", session, stmt, nil) require.NoError(t, err) @@ -357,7 +359,7 @@ func TestExecutorAddSequenceDDL(t *testing.T) { // Should fail adding a table on a sharded keyspace ksSharded := "TestExecutor" - session = NewSafeSession(&vtgatepb.Session{TargetString: ksSharded}) + session = econtext.NewSafeSession(&vtgatepb.Session{TargetString: ksSharded}) stmt = "alter vschema add sequence sequence_table" _, err = executor.Execute(ctx, nil, "TestExecute", session, stmt, nil) @@ -403,7 +405,7 @@ func TestExecutorDropSequenceDDL(t *testing.T) { t.Fatalf("test_seq should not exist in original vschema") } - session := NewSafeSession(&vtgatepb.Session{TargetString: ks}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: ks}) // add test sequence stmt := "alter vschema add sequence test_seq" @@ -428,7 +430,7 @@ func TestExecutorDropSequenceDDL(t *testing.T) { } // Should fail dropping a non-existing test sequence - session = NewSafeSession(&vtgatepb.Session{TargetString: ks}) + session = econtext.NewSafeSession(&vtgatepb.Session{TargetString: ks}) stmt = "alter vschema drop sequence test_seq" _, err = executor.Execute(ctx, nil, "TestExecute", session, stmt, nil) @@ -447,7 +449,7 @@ func TestExecutorDropAutoIncDDL(t *testing.T) { executor, _, _, _, ctx := createExecutorEnv(t) ks := KsTestUnsharded - session := NewSafeSession(&vtgatepb.Session{TargetString: ks}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: ks}) stmt := "alter vschema add table test_table" _, err := executor.Execute(ctx, nil, "TestExecute", session, stmt, nil) @@ -488,7 +490,7 @@ func TestExecutorAddDropVindexDDL(t *testing.T) { }() executor, sbc1, sbc2, sbclookup, ctx := createExecutorEnv(t) ks := "TestExecutor" - session := NewSafeSession(&vtgatepb.Session{TargetString: ks}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: ks}) vschemaUpdates := make(chan *vschemapb.SrvVSchema, 4) executor.serv.WatchSrvVSchema(ctx, "aa", func(vschema *vschemapb.SrvVSchema, err error) bool { vschemaUpdates <- vschema @@ -706,7 +708,7 @@ func TestExecutorAddDropVindexDDL(t *testing.T) { require.EqualError(t, err, "table TestExecutor.nonexistent not defined in vschema") stmt = "alter vschema on nonexistent drop vindex test_lookup" - _, err = executor.Execute(ctx, nil, "TestExecute", NewSafeSession(&vtgatepb.Session{TargetString: "InvalidKeyspace"}), stmt, nil) + _, err = executor.Execute(ctx, nil, "TestExecute", econtext.NewSafeSession(&vtgatepb.Session{TargetString: "InvalidKeyspace"}), stmt, nil) require.EqualError(t, err, "VT05003: unknown database 'InvalidKeyspace' in vschema") stmt = "alter vschema on nowhere.nohow drop vindex test_lookup" @@ -731,7 +733,7 @@ func TestPlanExecutorVindexDDLACL(t *testing.T) { // t.Skip("not yet planned") executor, _, _, _, ctx := createExecutorEnv(t) ks := "TestExecutor" - session := NewSafeSession(&vtgatepb.Session{TargetString: ks}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: ks}) ctxRedUser := callerid.NewContext(ctx, &vtrpcpb.CallerID{}, &querypb.VTGateCallerID{Username: "redUser"}) ctxBlueUser := callerid.NewContext(ctx, &vtrpcpb.CallerID{}, &querypb.VTGateCallerID{Username: "blueUser"}) diff --git a/go/vt/vtgate/executor_vstream_test.go b/go/vt/vtgate/executor_vstream_test.go index 5466e9e8f3f..22fb7ee1034 100644 --- a/go/vt/vtgate/executor_vstream_test.go +++ b/go/vt/vtgate/executor_vstream_test.go @@ -21,6 +21,7 @@ import ( "time" "vitess.io/vitess/go/vt/vtgate/engine" + econtext "vitess.io/vitess/go/vt/vtgate/executorcontext" querypb "vitess.io/vitess/go/vt/proto/query" @@ -76,7 +77,7 @@ func TestVStreamSQLUnsharded(t *testing.T) { results := make(chan *sqltypes.Result, 20) go func() { - err := executor.StreamExecute(ctx, nil, "TestExecuteStream", NewAutocommitSession(&vtgatepb.Session{TargetString: KsTestUnsharded}), sql, nil, func(qr *sqltypes.Result) error { + err := executor.StreamExecute(ctx, nil, "TestExecuteStream", econtext.NewAutocommitSession(&vtgatepb.Session{TargetString: KsTestUnsharded}), sql, nil, func(qr *sqltypes.Result) error { results <- qr return nil }) diff --git a/go/vt/vtgate/executorcontext/faketopo.go b/go/vt/vtgate/executorcontext/faketopo.go new file mode 100644 index 00000000000..f61119dce15 --- /dev/null +++ b/go/vt/vtgate/executorcontext/faketopo.go @@ -0,0 +1,68 @@ +/* +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 executorcontext + +import ( + "context" + "encoding/hex" + + topodatapb "vitess.io/vitess/go/vt/proto/topodata" + vschemapb "vitess.io/vitess/go/vt/proto/vschema" + "vitess.io/vitess/go/vt/topo" +) + +type FakeTopoServer struct{} + +// GetTopoServer returns the full topo.Server instance. +func (f *FakeTopoServer) GetTopoServer() (*topo.Server, error) { + return nil, nil +} + +// GetSrvKeyspaceNames returns the list of keyspaces served in +// the provided cell. +func (f *FakeTopoServer) GetSrvKeyspaceNames(ctx context.Context, cell string, staleOK bool) ([]string, error) { + return []string{"ks1"}, nil +} + +// GetSrvKeyspace returns the SrvKeyspace for a cell/keyspace. +func (f *FakeTopoServer) GetSrvKeyspace(ctx context.Context, cell, keyspace string) (*topodatapb.SrvKeyspace, error) { + zeroHexBytes, _ := hex.DecodeString("") + eightyHexBytes, _ := hex.DecodeString("80") + ks := &topodatapb.SrvKeyspace{ + Partitions: []*topodatapb.SrvKeyspace_KeyspacePartition{ + { + ServedType: topodatapb.TabletType_PRIMARY, + ShardReferences: []*topodatapb.ShardReference{ + {Name: "-80", KeyRange: &topodatapb.KeyRange{Start: zeroHexBytes, End: eightyHexBytes}}, + {Name: "80-", KeyRange: &topodatapb.KeyRange{Start: eightyHexBytes, End: zeroHexBytes}}, + }, + }, + }, + } + return ks, nil +} + +func (f *FakeTopoServer) WatchSrvKeyspace(ctx context.Context, cell, keyspace string, callback func(*topodatapb.SrvKeyspace, error) bool) { + ks, err := f.GetSrvKeyspace(ctx, cell, keyspace) + callback(ks, err) +} + +// WatchSrvVSchema starts watching the SrvVSchema object for +// the provided cell. It will call the callback when +// a new value or an error occurs. +func (f *FakeTopoServer) WatchSrvVSchema(ctx context.Context, cell string, callback func(*vschemapb.SrvVSchema, error) bool) { +} diff --git a/go/vt/vtgate/safe_session.go b/go/vt/vtgate/executorcontext/safe_session.go similarity index 78% rename from go/vt/vtgate/safe_session.go rename to go/vt/vtgate/executorcontext/safe_session.go index 1d57c63ef35..c77bba76ff8 100644 --- a/go/vt/vtgate/safe_session.go +++ b/go/vt/vtgate/executorcontext/safe_session.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package vtgate +package executorcontext import ( "fmt" @@ -23,22 +23,19 @@ import ( "sync" "time" - "vitess.io/vitess/go/sqltypes" - "google.golang.org/protobuf/proto" "vitess.io/vitess/go/mysql/datetime" - + "vitess.io/vitess/go/sqltypes" + querypb "vitess.io/vitess/go/vt/proto/query" + topodatapb "vitess.io/vitess/go/vt/proto/topodata" + vtgatepb "vitess.io/vitess/go/vt/proto/vtgate" + vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/srvtopo" "vitess.io/vitess/go/vt/sysvars" "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vtgate/engine" - - querypb "vitess.io/vitess/go/vt/proto/query" - topodatapb "vitess.io/vitess/go/vt/proto/topodata" - vtgatepb "vitess.io/vitess/go/vt/proto/vtgate" - vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" ) type ( @@ -58,7 +55,7 @@ type ( rollbackOnPartialExec string savepointName string - // this is a signal that found_rows has already been handles by the primitives, + // this is a signal that found_rows has already been handled by the primitives, // and doesn't have to be updated by the executor foundRowsHandled bool @@ -66,12 +63,12 @@ type ( // as the query that started a new transaction on the shard belong to a vindex. queryFromVindex bool - logging *executeLogger + logging *ExecuteLogger *vtgatepb.Session } - executeLogger struct { + ExecuteLogger struct { mu sync.Mutex entries []engine.ExecuteEntry lastID int @@ -127,6 +124,8 @@ const ( savepointRollback ) +const TxRollback = "Rollback Transaction" + // NewSafeSession returns a new SafeSession based on the Session func NewSafeSession(sessn *vtgatepb.Session) *SafeSession { if sessn == nil { @@ -205,6 +204,50 @@ func (session *SafeSession) resetCommonLocked() { } } +// NewAutocommitSession returns a SafeSession based on the original +// session, but with autocommit enabled. +func (session *SafeSession) NewAutocommitSession() *SafeSession { + ss := NewAutocommitSession(session.Session) + ss.logging = session.logging + return ss +} + +// IsFoundRowsHandled returns the foundRowsHandled. +func (session *SafeSession) IsFoundRowsHandled() bool { + session.mu.Lock() + defer session.mu.Unlock() + return session.foundRowsHandled +} + +// SetFoundRows set the found rows value. +func (session *SafeSession) SetFoundRows(value uint64) { + session.mu.Lock() + defer session.mu.Unlock() + session.FoundRows = value + session.foundRowsHandled = true +} + +// GetRollbackOnPartialExec returns the rollbackOnPartialExec value. +func (session *SafeSession) GetRollbackOnPartialExec() string { + session.mu.Lock() + defer session.mu.Unlock() + return session.rollbackOnPartialExec +} + +// SetQueryFromVindex set the queryFromVindex value. +func (session *SafeSession) SetQueryFromVindex(value bool) { + session.mu.Lock() + defer session.mu.Unlock() + session.queryFromVindex = value +} + +// GetQueryFromVindex returns the queryFromVindex value. +func (session *SafeSession) GetQueryFromVindex() bool { + session.mu.Lock() + defer session.mu.Unlock() + return session.queryFromVindex +} + // SetQueryTimeout sets the query timeout func (session *SafeSession) SetQueryTimeout(queryTimeout int64) { session.mu.Lock() @@ -312,7 +355,7 @@ func (session *SafeSession) SetRollbackCommand() { if session.savepointState == savepointSet { session.rollbackOnPartialExec = fmt.Sprintf("rollback to %s", session.savepointName) } else { - session.rollbackOnPartialExec = txRollback + session.rollbackOnPartialExec = TxRollback } session.savepointState = savepointRollbackSet } @@ -340,6 +383,18 @@ func (session *SafeSession) SetCommitOrder(co vtgatepb.CommitOrder) { session.commitOrder = co } +// GetCommitOrder returns the commit order. +func (session *SafeSession) GetCommitOrder() vtgatepb.CommitOrder { + session.mu.Lock() + defer session.mu.Unlock() + return session.commitOrder +} + +// GetLogger returns executor logger. +func (session *SafeSession) GetLogger() *ExecuteLogger { + return session.logging +} + // InTransaction returns true if we are in a transaction func (session *SafeSession) InTransaction() bool { session.mu.Lock() @@ -347,73 +402,88 @@ func (session *SafeSession) InTransaction() bool { return session.Session.InTransaction } -// FindAndChangeSessionIfInSingleTxMode returns the transactionId and tabletAlias, if any, for a session -// modifies the shard session in a specific case for single mode transaction. -func (session *SafeSession) FindAndChangeSessionIfInSingleTxMode(keyspace, shard string, tabletType topodatapb.TabletType, txMode vtgatepb.TransactionMode) (int64, int64, *topodatapb.TabletAlias, error) { +// FindAndChangeSessionIfInSingleTxMode retrieves the ShardSession matching the given keyspace, shard, and tablet type. +// It performs additional checks and may modify the ShardSession in specific cases for single-mode transactions. +// +// Key behavior: +// 1. Retrieves the appropriate list of sessions (PreSessions, PostSessions, or default ShardSessions) based on the commit order. +// 2. Identifies a matching session by keyspace, shard, and tablet type. +// 3. If the session meets specific conditions (e.g., non-vindex-only, single transaction mode), it updates the session state: +// - Converts a vindex-only session to a standard session if required by the transaction type. +// - If a multi-shard transaction is detected in Single mode, marks the session for rollback and returns an error. +// +// Parameters: +// - keyspace: The keyspace of the target shard. +// - shard: The shard name of the target. +// - tabletType: The type of the tablet for the shard session. +// - txMode: The transaction mode (e.g., Single, Multi). +// +// Returns: +// - The matching ShardSession, if found and valid for the operation. +// - An error if a Single-mode transaction attempts to span multiple shards. +func (session *SafeSession) FindAndChangeSessionIfInSingleTxMode(keyspace, shard string, tabletType topodatapb.TabletType, txMode vtgatepb.TransactionMode) (*vtgatepb.Session_ShardSession, error) { session.mu.Lock() defer session.mu.Unlock() - sessions := session.ShardSessions + + shardSession := session.findSessionLocked(keyspace, shard, tabletType) + + if shardSession == nil { + return nil, nil + } + + if !shardSession.VindexOnly { + return shardSession, nil + } + + if err := session.singleModeErrorOnCrossShard(txMode, 0); err != nil { + return nil, err + } + + // the shard session is now used by non-vindex query as well, + // so it is not an exclusive vindex only shard session anymore. + shardSession.VindexOnly = false + return shardSession, nil +} + +func (session *SafeSession) findSessionLocked(keyspace, shard string, tabletType topodatapb.TabletType) *vtgatepb.Session_ShardSession { + // Select the appropriate session list based on the commit order. + var sessions []*vtgatepb.Session_ShardSession switch session.commitOrder { case vtgatepb.CommitOrder_PRE: sessions = session.PreSessions case vtgatepb.CommitOrder_POST: sessions = session.PostSessions + default: + sessions = session.ShardSessions } + + // Find and return the matching shard session. for _, shardSession := range sessions { - if keyspace == shardSession.Target.Keyspace && tabletType == shardSession.Target.TabletType && shard == shardSession.Target.Shard { - if txMode != vtgatepb.TransactionMode_SINGLE || !shardSession.VindexOnly || session.queryFromVindex { - return shardSession.TransactionId, shardSession.ReservedId, shardSession.TabletAlias, nil - } - count := actualNoOfShardSession(session.ShardSessions) - // If the count of shard session which are non vindex only is greater than 0, then it is a - if count > 0 { - session.mustRollback = true - return 0, 0, nil, vterrors.Errorf(vtrpcpb.Code_ABORTED, "multi-db transaction attempted: %v", session.ShardSessions) - } - // the shard session is now used by non-vindex query as well, - // so it is not an exclusive vindex only shard session anymore. - shardSession.VindexOnly = false - return shardSession.TransactionId, shardSession.ReservedId, shardSession.TabletAlias, nil + if shardSession.Target.Keyspace == keyspace && + shardSession.Target.Shard == shard && + shardSession.Target.TabletType == tabletType { + return shardSession } } - return 0, 0, nil, nil -} - -func addOrUpdate(shardSession *vtgatepb.Session_ShardSession, sessions []*vtgatepb.Session_ShardSession) ([]*vtgatepb.Session_ShardSession, error) { - appendSession := true - for i, sess := range sessions { - targetedAtSameTablet := sess.Target.Keyspace == shardSession.Target.Keyspace && - sess.Target.TabletType == shardSession.Target.TabletType && - sess.Target.Shard == shardSession.Target.Shard - if targetedAtSameTablet { - if !proto.Equal(sess.TabletAlias, shardSession.TabletAlias) { - errorDetails := fmt.Sprintf("got non-matching aliases (%v vs %v) for the same target (keyspace: %v, tabletType: %v, shard: %v)", - sess.TabletAlias, shardSession.TabletAlias, - sess.Target.Keyspace, sess.Target.TabletType, sess.Target.Shard) - return nil, vterrors.New(vtrpcpb.Code_FAILED_PRECONDITION, errorDetails) - } - // replace the old info with the new one - sessions[i] = shardSession - appendSession = false - break - } - } - if appendSession { - sessions = append(sessions, shardSession) - } + return nil +} - return sessions, nil +type ShardActionInfo interface { + TransactionID() int64 + ReservedID() int64 + RowsAffected() bool + Alias() *topodatapb.TabletAlias } // AppendOrUpdate adds a new ShardSession, or updates an existing one if one already exists for the given shard session -func (session *SafeSession) AppendOrUpdate(shardSession *vtgatepb.Session_ShardSession, txMode vtgatepb.TransactionMode) error { +func (session *SafeSession) AppendOrUpdate(target *querypb.Target, info ShardActionInfo, existingSession *vtgatepb.Session_ShardSession, txMode vtgatepb.TransactionMode) error { session.mu.Lock() defer session.mu.Unlock() // additional check of transaction id is required // as now in autocommit mode there can be session due to reserved connection // that needs to be stored as shard session. - if session.autocommitState == autocommitted && shardSession.TransactionId != 0 { + if session.autocommitState == autocommitted && info.TransactionID() != 0 { // Should be unreachable return vterrors.VT13001("unexpected 'autocommitted' state in transaction") } @@ -423,45 +493,62 @@ func (session *SafeSession) AppendOrUpdate(shardSession *vtgatepb.Session_ShardS } session.autocommitState = notAutocommittable - // Always append, in order for rollback to succeed. - switch session.commitOrder { - case vtgatepb.CommitOrder_NORMAL: - if session.queryFromVindex { - shardSession.VindexOnly = true + if existingSession != nil { + existingSession.TransactionId = info.TransactionID() + existingSession.ReservedId = info.ReservedID() + if !existingSession.RowsAffected { + existingSession.RowsAffected = info.RowsAffected() } - newSessions, err := addOrUpdate(shardSession, session.ShardSessions) - if err != nil { + if existingSession.VindexOnly { + existingSession.VindexOnly = session.queryFromVindex + } + if err := session.singleModeErrorOnCrossShard(txMode, 1); err != nil { return err } - session.ShardSessions = newSessions + return nil + } + newSession := &vtgatepb.Session_ShardSession{ + Target: target, + TabletAlias: info.Alias(), + TransactionId: info.TransactionID(), + ReservedId: info.ReservedID(), + RowsAffected: info.RowsAffected(), + VindexOnly: session.queryFromVindex, + } - if session.queryFromVindex { - break - } - // isSingle is enforced only for normal commit order operations. - if session.isSingleDB(txMode) && len(session.ShardSessions) > 1 { - count := actualNoOfShardSession(session.ShardSessions) - if count <= 1 { - break - } - session.mustRollback = true - return vterrors.Errorf(vtrpcpb.Code_ABORTED, "multi-db transaction attempted: %v", session.ShardSessions) - } - case vtgatepb.CommitOrder_PRE: - newSessions, err := addOrUpdate(shardSession, session.PreSessions) - if err != nil { + // Always append, in order for rollback to succeed. + switch session.commitOrder { + case vtgatepb.CommitOrder_NORMAL: + session.ShardSessions = append(session.ShardSessions, newSession) + if err := session.singleModeErrorOnCrossShard(txMode, 1); err != nil { return err } - session.PreSessions = newSessions + case vtgatepb.CommitOrder_PRE: + session.PreSessions = append(session.PreSessions, newSession) case vtgatepb.CommitOrder_POST: - newSessions, err := addOrUpdate(shardSession, session.PostSessions) - if err != nil { - return err - } - session.PostSessions = newSessions + session.PostSessions = append(session.PostSessions, newSession) default: // Should be unreachable - return vterrors.Errorf(vtrpcpb.Code_INTERNAL, "[BUG] SafeSession.AppendOrUpdate: unexpected commitOrder") + return vterrors.VT13001(fmt.Sprintf("unexpected commitOrder to append shard session: %v", session.commitOrder)) + } + + return nil +} + +// singleModeErrorOnCrossShard checks if a transaction violates the Single mode constraint by spanning multiple shards. +func (session *SafeSession) singleModeErrorOnCrossShard(txMode vtgatepb.TransactionMode, exceedsCrossShard int) error { + // Skip the check if: + // 1. The query comes from a lookup vindex. + // 2. The transaction mode is not Single. + // 3. The transaction is not in the normal shard session. + if session.queryFromVindex || session.commitOrder != vtgatepb.CommitOrder_NORMAL || !session.isSingleDB(txMode) { + return nil + } + + // If the transaction spans multiple shards, abort it. + if actualNoOfShardSession(session.ShardSessions) > exceedsCrossShard { + session.mustRollback = true // Mark the session for rollback. + return vterrors.Errorf(vtrpcpb.Code_ABORTED, "multi-db transaction attempted: %v", session.ShardSessions) } return nil @@ -678,12 +765,11 @@ func (session *SafeSession) UpdateLockHeartbeat() { session.LastLockHeartbeat = time.Now().Unix() } -// TriggerLockHeartBeat returns if it time to trigger next lock heartbeat -func (session *SafeSession) TriggerLockHeartBeat() bool { +// GetLockHeartbeat returns last time the lock heartbeat was sent. +func (session *SafeSession) GetLockHeartbeat() int64 { session.mu.Lock() defer session.mu.Unlock() - now := time.Now().Unix() - return now-session.LastLockHeartbeat >= int64(lockHeartbeatTime.Seconds()) + return session.LastLockHeartbeat } // InLockSession returns whether locking is used on this session. @@ -836,9 +922,7 @@ func (session *SafeSession) GetOrCreateOptions() *querypb.ExecuteOptions { return session.Session.Options } -var _ iQueryOption = (*SafeSession)(nil) - -func (session *SafeSession) cachePlan() bool { +func (session *SafeSession) CachePlan() bool { if session == nil || session.Options == nil { return true } @@ -849,7 +933,7 @@ func (session *SafeSession) cachePlan() bool { return !(session.Options.SkipQueryPlanCache || session.Options.HasCreatedTempTables) } -func (session *SafeSession) getSelectLimit() int { +func (session *SafeSession) GetSelectLimit() int { if session == nil || session.Options == nil { return -1 } @@ -860,16 +944,16 @@ func (session *SafeSession) getSelectLimit() int { return int(session.Options.SqlSelectLimit) } -// isTxOpen returns true if there is open connection to any of the shard. -func (session *SafeSession) isTxOpen() bool { +// IsTxOpen returns true if there is open connection to any of the shard. +func (session *SafeSession) IsTxOpen() bool { session.mu.Lock() defer session.mu.Unlock() return len(session.ShardSessions) > 0 || len(session.PreSessions) > 0 || len(session.PostSessions) > 0 } -// getSessions returns the shard session for the current commit order. -func (session *SafeSession) getSessions() []*vtgatepb.Session_ShardSession { +// GetSessions returns the shard session for the current commit order. +func (session *SafeSession) GetSessions() []*vtgatepb.Session_ShardSession { session.mu.Lock() defer session.mu.Unlock() @@ -956,7 +1040,7 @@ func (session *SafeSession) EnableLogging(parser *sqlparser.Parser) { session.mu.Lock() defer session.mu.Unlock() - session.logging = &executeLogger{ + session.logging = &ExecuteLogger{ parser: parser, } } @@ -994,7 +1078,15 @@ func (session *SafeSession) GetPrepareData(name string) *vtgatepb.PrepareData { return session.PrepareStatement[name] } -func (l *executeLogger) log(primitive engine.Primitive, target *querypb.Target, gateway srvtopo.Gateway, query string, begin bool, bv map[string]*querypb.BindVariable) { +func (session *SafeSession) Log(primitive engine.Primitive, target *querypb.Target, gateway srvtopo.Gateway, query string, begin bool, bv map[string]*querypb.BindVariable) { + session.logging.Log(primitive, target, gateway, query, begin, bv) +} + +func (session *SafeSession) GetLogs() []engine.ExecuteEntry { + return session.logging.GetLogs() +} + +func (l *ExecuteLogger) Log(primitive engine.Primitive, target *querypb.Target, gateway srvtopo.Gateway, query string, begin bool, bv map[string]*querypb.BindVariable) { if l == nil { return } @@ -1033,7 +1125,10 @@ func (l *executeLogger) log(primitive engine.Primitive, target *querypb.Target, }) } -func (l *executeLogger) GetLogs() []engine.ExecuteEntry { +func (l *ExecuteLogger) GetLogs() []engine.ExecuteEntry { + if l == nil { + return nil + } l.mu.Lock() defer l.mu.Unlock() result := make([]engine.ExecuteEntry, len(l.entries)) diff --git a/go/vt/vtgate/executorcontext/safe_session_test.go b/go/vt/vtgate/executorcontext/safe_session_test.go new file mode 100644 index 00000000000..14ea2ad9dac --- /dev/null +++ b/go/vt/vtgate/executorcontext/safe_session_test.go @@ -0,0 +1,201 @@ +/* +Copyright 2020 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 executorcontext + +import ( + "reflect" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + querypb "vitess.io/vitess/go/vt/proto/query" + topodatapb "vitess.io/vitess/go/vt/proto/topodata" + vtgatepb "vitess.io/vitess/go/vt/proto/vtgate" +) + +type fakeInfo struct { + transactionID int64 + alias *topodatapb.TabletAlias +} + +func (s *fakeInfo) TransactionID() int64 { + return s.transactionID +} + +func (s *fakeInfo) ReservedID() int64 { + return 0 +} + +func (s *fakeInfo) RowsAffected() bool { + return false +} + +func (s *fakeInfo) Alias() *topodatapb.TabletAlias { + return s.alias +} + +func info(txId, uid int) ShardActionInfo { + return &fakeInfo{transactionID: int64(txId), alias: &topodatapb.TabletAlias{Cell: "cell", Uid: uint32(uid)}} +} + +// TestFailToMultiShardWhenSetToSingleDb tests that single db transactions fails on going multi shard. +func TestFailToMultiShardWhenSetToSingleDb(t *testing.T) { + session := NewSafeSession(&vtgatepb.Session{ + InTransaction: true, TransactionMode: vtgatepb.TransactionMode_SINGLE, + }) + + err := session.AppendOrUpdate( + &querypb.Target{Keyspace: "keyspace", Shard: "0"}, + info(1, 0), + nil, + vtgatepb.TransactionMode_SINGLE) + require.NoError(t, err) + err = session.AppendOrUpdate( + &querypb.Target{Keyspace: "keyspace", Shard: "1"}, + info(1, 1), + nil, + vtgatepb.TransactionMode_SINGLE) + require.Error(t, err) +} + +// TestSingleDbUpdateToMultiShard tests that a single db transaction cannot be updated to multi shard. +func TestSingleDbUpdateToMultiShard(t *testing.T) { + session := NewSafeSession(&vtgatepb.Session{ + InTransaction: true, TransactionMode: vtgatepb.TransactionMode_SINGLE, + }) + + // shard session s0 due to a vindex query + session.queryFromVindex = true + err := session.AppendOrUpdate( + &querypb.Target{Keyspace: "keyspace", Shard: "0"}, + info(1, 0), + nil, + vtgatepb.TransactionMode_SINGLE) + require.NoError(t, err) + session.queryFromVindex = false + + // shard session s1 + err = session.AppendOrUpdate( + &querypb.Target{Keyspace: "keyspace", Shard: "1"}, + info(1, 1), + nil, + vtgatepb.TransactionMode_SINGLE) + require.NoError(t, err) + + // shard session s0 with normal query + err = session.AppendOrUpdate( + &querypb.Target{Keyspace: "keyspace", Shard: "0"}, + info(1, 1), + session.ShardSessions[0], + vtgatepb.TransactionMode_SINGLE) + require.Error(t, err) +} + +// TestSingleDbPreFailOnFind tests that finding a shard session fails +// if already shard session exists on another shard and the query is not from vindex. +func TestSingleDbPreFailOnFind(t *testing.T) { + session := NewSafeSession(&vtgatepb.Session{ + InTransaction: true, TransactionMode: vtgatepb.TransactionMode_SINGLE, + }) + + // shard session s0 due to a vindex query + session.queryFromVindex = true + err := session.AppendOrUpdate( + &querypb.Target{Keyspace: "keyspace", Shard: "0"}, + info(1, 0), + nil, + vtgatepb.TransactionMode_SINGLE) + require.NoError(t, err) + session.queryFromVindex = false + + // shard session s1 + err = session.AppendOrUpdate( + &querypb.Target{Keyspace: "keyspace", Shard: "1"}, + info(1, 1), + nil, + vtgatepb.TransactionMode_SINGLE) + require.NoError(t, err) + + // shard session s1 for normal query again - should not fail as already part of the session. + ss, err := session.FindAndChangeSessionIfInSingleTxMode( + "keyspace", + "1", + topodatapb.TabletType_UNKNOWN, + vtgatepb.TransactionMode_SINGLE) + require.NoError(t, err) + require.NotNil(t, ss) + require.False(t, ss.VindexOnly) + require.EqualValues(t, 1, ss.TabletAlias.Uid) + + // shard session s0 for normal query + _, err = session.FindAndChangeSessionIfInSingleTxMode( + "keyspace", + "0", + topodatapb.TabletType_UNKNOWN, + vtgatepb.TransactionMode_SINGLE) + require.Error(t, err) +} + +func TestPrequeries(t *testing.T) { + session := NewSafeSession(&vtgatepb.Session{ + SystemVariables: map[string]string{ + "s1": "'apa'", + "s2": "42", + }, + }) + + want := []string{"set s1 = 'apa', s2 = 42"} + preQueries := session.SetPreQueries() + + if !reflect.DeepEqual(want, preQueries) { + t.Errorf("got %v but wanted %v", preQueries, want) + } +} + +func TestTimeZone(t *testing.T) { + testCases := []struct { + tz string + want string + }{ + { + tz: "'Europe/Amsterdam'", + want: "Europe/Amsterdam", + }, + { + tz: "'+02:00'", + want: "UTC+02:00", + }, + { + tz: "foo", + want: (*time.Location)(nil).String(), + }, + } + + for _, tc := range testCases { + t.Run(tc.tz, func(t *testing.T) { + session := NewSafeSession(&vtgatepb.Session{ + SystemVariables: map[string]string{ + "time_zone": tc.tz, + }, + }) + + assert.Equal(t, tc.want, session.TimeZone().String()) + }) + } +} diff --git a/go/vt/vtgate/vcursor_impl.go b/go/vt/vtgate/executorcontext/vcursor_impl.go similarity index 66% rename from go/vt/vtgate/vcursor_impl.go rename to go/vt/vtgate/executorcontext/vcursor_impl.go index b916f171e05..c1f341b38cf 100644 --- a/go/vt/vtgate/vcursor_impl.go +++ b/go/vt/vtgate/executorcontext/vcursor_impl.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package vtgate +package executorcontext import ( "context" @@ -26,6 +26,7 @@ import ( "time" "github.com/google/uuid" + "golang.org/x/exp/maps" "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/mysql/config" @@ -60,38 +61,62 @@ import ( ) var ( - _ engine.VCursor = (*vcursorImpl)(nil) - _ plancontext.VSchema = (*vcursorImpl)(nil) - _ iExecute = (*Executor)(nil) - _ vindexes.VCursor = (*vcursorImpl)(nil) + _ engine.VCursor = (*VCursorImpl)(nil) + _ plancontext.VSchema = (*VCursorImpl)(nil) + _ vindexes.VCursor = (*VCursorImpl)(nil) ) +var ErrNoKeyspace = vterrors.VT09005() + type ( + ResultsObserver interface { + Observe(*sqltypes.Result) + } + + VCursorConfig struct { + Collation collations.ID + + MaxMemoryRows int + EnableShardRouting bool + DefaultTabletType topodatapb.TabletType + QueryTimeout int + DBDDLPlugin string + ForeignKeyMode vschemapb.Keyspace_ForeignKeyMode + SetVarEnabled bool + EnableViews bool + WarnShardedOnly bool + PlannerVersion plancontext.PlannerVersion + + WarmingReadsPercent int + WarmingReadsTimeout time.Duration + WarmingReadsChannel chan bool + } + // vcursor_impl needs these facilities to be able to be able to execute queries for vindexes iExecute interface { Execute(ctx context.Context, mysqlCtx vtgateservice.MySQLConnection, method string, session *SafeSession, s string, vars map[string]*querypb.BindVariable) (*sqltypes.Result, error) - ExecuteMultiShard(ctx context.Context, primitive engine.Primitive, rss []*srvtopo.ResolvedShard, queries []*querypb.BoundQuery, session *SafeSession, autocommit bool, ignoreMaxMemoryRows bool, resultsObserver resultsObserver) (qr *sqltypes.Result, errs []error) - StreamExecuteMulti(ctx context.Context, primitive engine.Primitive, query string, rss []*srvtopo.ResolvedShard, vars []map[string]*querypb.BindVariable, session *SafeSession, autocommit bool, callback func(reply *sqltypes.Result) error, observer resultsObserver) []error + ExecuteMultiShard(ctx context.Context, primitive engine.Primitive, rss []*srvtopo.ResolvedShard, queries []*querypb.BoundQuery, session *SafeSession, autocommit bool, ignoreMaxMemoryRows bool, resultsObserver ResultsObserver) (qr *sqltypes.Result, errs []error) + StreamExecuteMulti(ctx context.Context, primitive engine.Primitive, query string, rss []*srvtopo.ResolvedShard, vars []map[string]*querypb.BindVariable, session *SafeSession, autocommit bool, callback func(reply *sqltypes.Result) error, observer ResultsObserver) []error ExecuteLock(ctx context.Context, rs *srvtopo.ResolvedShard, query *querypb.BoundQuery, session *SafeSession, lockFuncType sqlparser.LockingFuncType) (*sqltypes.Result, error) Commit(ctx context.Context, safeSession *SafeSession) error ExecuteMessageStream(ctx context.Context, rss []*srvtopo.ResolvedShard, name string, callback func(*sqltypes.Result) error) error ExecuteVStream(ctx context.Context, rss []*srvtopo.ResolvedShard, filter *binlogdatapb.Filter, gtid string, callback func(evs []*binlogdatapb.VEvent) error) error ReleaseLock(ctx context.Context, session *SafeSession) error - showVitessReplicationStatus(ctx context.Context, filter *sqlparser.ShowFilter) (*sqltypes.Result, error) - showShards(ctx context.Context, filter *sqlparser.ShowFilter, destTabletType topodatapb.TabletType) (*sqltypes.Result, error) - showTablets(filter *sqlparser.ShowFilter) (*sqltypes.Result, error) - showVitessMetadata(ctx context.Context, filter *sqlparser.ShowFilter) (*sqltypes.Result, error) - setVitessMetadata(ctx context.Context, name, value string) error + ShowVitessReplicationStatus(ctx context.Context, filter *sqlparser.ShowFilter) (*sqltypes.Result, error) + ShowShards(ctx context.Context, filter *sqlparser.ShowFilter, destTabletType topodatapb.TabletType) (*sqltypes.Result, error) + ShowTablets(filter *sqlparser.ShowFilter) (*sqltypes.Result, error) + ShowVitessMetadata(ctx context.Context, filter *sqlparser.ShowFilter) (*sqltypes.Result, error) + SetVitessMetadata(ctx context.Context, name, value string) error // TODO: remove when resolver is gone - ParseDestinationTarget(targetString string) (string, topodatapb.TabletType, key.Destination, error) VSchema() *vindexes.VSchema - planPrepareStmt(ctx context.Context, vcursor *vcursorImpl, query string) (*engine.Plan, sqlparser.Statement, error) + PlanPrepareStmt(ctx context.Context, vcursor *VCursorImpl, query string) (*engine.Plan, sqlparser.Statement, error) - environment() *vtenv.Environment + Environment() *vtenv.Environment ReadTransaction(ctx context.Context, transactionID string) (*querypb.TransactionMetadata, error) UnresolvedTransactions(ctx context.Context, targets []*querypb.Target) ([]*querypb.TransactionMetadata, error) + AddWarningCount(name string, value int64) } // VSchemaOperator is an interface to Vschema Operations @@ -100,10 +125,11 @@ type ( UpdateVSchema(ctx context.Context, ksName string, vschema *vschemapb.SrvVSchema) error } - // vcursorImpl implements the VCursor functionality used by dependent + // VCursorImpl implements the VCursor functionality used by dependent // packages to call back into VTGate. - vcursorImpl struct { - safeSession *SafeSession + VCursorImpl struct { + config VCursorConfig + SafeSession *SafeSession keyspace string tabletType topodatapb.TabletType destination key.Destination @@ -112,7 +138,6 @@ type ( resolver *srvtopo.Resolver topoServer *topo.Server logStats *logstats.LogStats - collation collations.ID // fkChecksState stores the state of foreign key checks variable. // This state is meant to be the final fk checks state after consulting the @@ -123,16 +148,11 @@ type ( vschema *vindexes.VSchema vm VSchemaOperator semTable *semantics.SemTable - warnShardedOnly bool // when using sharded only features, a warning will be warnings field queryTimeout time.Duration warnings []*querypb.QueryWarning // any warnings that are accumulated during the planning phase are stored here - pv plancontext.PlannerVersion - warmingReadsPercent int - warmingReadsChannel chan bool - - resultsObserver resultsObserver + observer ResultsObserver // this is a map of the number of rows that every primitive has returned // if this field is nil, it means that we are not logging operator traffic @@ -141,23 +161,23 @@ type ( } ) -// newVcursorImpl creates a vcursorImpl. Before creating this object, you have to separate out any marginComments that came with +// NewVCursorImpl creates a VCursorImpl. Before creating this object, you have to separate out any marginComments that came with // the query and supply it here. Trailing comments are typically sent by the application for various reasons, // including as identifying markers. So, they have to be added back to all queries that are executed // on behalf of the original query. -func newVCursorImpl( +func NewVCursorImpl( safeSession *SafeSession, marginComments sqlparser.MarginComments, - executor *Executor, + executor iExecute, logStats *logstats.LogStats, vm VSchemaOperator, vschema *vindexes.VSchema, resolver *srvtopo.Resolver, serv srvtopo.Server, - warnShardedOnly bool, - pv plancontext.PlannerVersion, -) (*vcursorImpl, error) { - keyspace, tabletType, destination, err := parseDestinationTarget(safeSession.TargetString, vschema) + observer ResultsObserver, + cfg VCursorConfig, +) (*VCursorImpl, error) { + keyspace, tabletType, destination, err := ParseDestinationTarget(safeSession.TargetString, cfg.DefaultTabletType, vschema) if err != nil { return nil, err } @@ -172,107 +192,175 @@ func newVCursorImpl( } } - // we only support collations for the new TabletGateway implementation - var connCollation collations.ID - if executor != nil { - if gw, isTabletGw := executor.resolver.resolver.GetGateway().(*TabletGateway); isTabletGw { - connCollation = gw.DefaultConnCollation() - } - } - if connCollation == collations.Unknown { - connCollation = executor.env.CollationEnv().DefaultConnectionCharset() - } - - warmingReadsPct := 0 - var warmingReadsChan chan bool - if executor != nil { - warmingReadsPct = executor.warmingReadsPercent - warmingReadsChan = executor.warmingReadsChannel - } - return &vcursorImpl{ - safeSession: safeSession, - keyspace: keyspace, - tabletType: tabletType, - destination: destination, - marginComments: marginComments, - executor: executor, - logStats: logStats, - collation: connCollation, - resolver: resolver, - vschema: vschema, - vm: vm, - topoServer: ts, - warnShardedOnly: warnShardedOnly, - pv: pv, - warmingReadsPercent: warmingReadsPct, - warmingReadsChannel: warmingReadsChan, - resultsObserver: nullResultsObserver{}, + return &VCursorImpl{ + config: cfg, + SafeSession: safeSession, + keyspace: keyspace, + tabletType: tabletType, + destination: destination, + marginComments: marginComments, + executor: executor, + logStats: logStats, + resolver: resolver, + vschema: vschema, + vm: vm, + topoServer: ts, + + observer: observer, }, nil } +func (vc *VCursorImpl) CloneForMirroring(ctx context.Context) engine.VCursor { + callerId := callerid.EffectiveCallerIDFromContext(ctx) + immediateCallerId := callerid.ImmediateCallerIDFromContext(ctx) + + clonedCtx := callerid.NewContext(ctx, callerId, immediateCallerId) + + v := &VCursorImpl{ + config: vc.config, + SafeSession: NewAutocommitSession(vc.SafeSession.Session), + keyspace: vc.keyspace, + tabletType: vc.tabletType, + destination: vc.destination, + marginComments: vc.marginComments, + executor: vc.executor, + resolver: vc.resolver, + topoServer: vc.topoServer, + logStats: &logstats.LogStats{Ctx: clonedCtx}, + ignoreMaxMemoryRows: vc.ignoreMaxMemoryRows, + vschema: vc.vschema, + vm: vc.vm, + semTable: vc.semTable, + warnings: vc.warnings, + observer: vc.observer, + } + + v.marginComments.Trailing += "/* mirror query */" + + return v +} + +func (vc *VCursorImpl) CloneForReplicaWarming(ctx context.Context) engine.VCursor { + callerId := callerid.EffectiveCallerIDFromContext(ctx) + immediateCallerId := callerid.ImmediateCallerIDFromContext(ctx) + + timedCtx, _ := context.WithTimeout(context.Background(), vc.config.WarmingReadsTimeout) // nolint + clonedCtx := callerid.NewContext(timedCtx, callerId, immediateCallerId) + + v := &VCursorImpl{ + config: vc.config, + SafeSession: NewAutocommitSession(vc.SafeSession.Session), + keyspace: vc.keyspace, + tabletType: topodatapb.TabletType_REPLICA, + destination: vc.destination, + marginComments: vc.marginComments, + executor: vc.executor, + resolver: vc.resolver, + topoServer: vc.topoServer, + logStats: &logstats.LogStats{Ctx: clonedCtx}, + + ignoreMaxMemoryRows: vc.ignoreMaxMemoryRows, + vschema: vc.vschema, + vm: vc.vm, + semTable: vc.semTable, + warnings: vc.warnings, + observer: vc.observer, + } + + v.marginComments.Trailing += "/* warming read */" + + return v +} + +func (vc *VCursorImpl) cloneWithAutocommitSession() *VCursorImpl { + safeSession := vc.SafeSession.NewAutocommitSession() + return &VCursorImpl{ + config: vc.config, + SafeSession: safeSession, + keyspace: vc.keyspace, + tabletType: vc.tabletType, + destination: vc.destination, + marginComments: vc.marginComments, + executor: vc.executor, + logStats: vc.logStats, + resolver: vc.resolver, + vschema: vc.vschema, + vm: vc.vm, + topoServer: vc.topoServer, + observer: vc.observer, + } +} + // HasSystemVariables returns whether the session has set system variables or not -func (vc *vcursorImpl) HasSystemVariables() bool { - return vc.safeSession.HasSystemVariables() +func (vc *VCursorImpl) HasSystemVariables() bool { + return vc.SafeSession.HasSystemVariables() } // GetSystemVariables takes a visitor function that will save each system variables of the session -func (vc *vcursorImpl) GetSystemVariables(f func(k string, v string)) { - vc.safeSession.GetSystemVariables(f) +func (vc *VCursorImpl) GetSystemVariables(f func(k string, v string)) { + vc.SafeSession.GetSystemVariables(f) +} + +// GetSystemVariablesCopy returns a copy of the system variables of the session. Changes to the original map will not affect the session. +func (vc *VCursorImpl) GetSystemVariablesCopy() map[string]string { + vc.SafeSession.mu.Lock() + defer vc.SafeSession.mu.Unlock() + return maps.Clone(vc.SafeSession.SystemVariables) } // ConnCollation returns the collation of this session -func (vc *vcursorImpl) ConnCollation() collations.ID { - return vc.collation +func (vc *VCursorImpl) ConnCollation() collations.ID { + return vc.config.Collation } // Environment returns the vtenv associated with this session -func (vc *vcursorImpl) Environment() *vtenv.Environment { - return vc.executor.environment() +func (vc *VCursorImpl) Environment() *vtenv.Environment { + return vc.executor.Environment() } -func (vc *vcursorImpl) TimeZone() *time.Location { - return vc.safeSession.TimeZone() +func (vc *VCursorImpl) TimeZone() *time.Location { + return vc.SafeSession.TimeZone() } -func (vc *vcursorImpl) SQLMode() string { +func (vc *VCursorImpl) SQLMode() string { // TODO: Implement return the current sql_mode. // This is currently hardcoded to the default in MySQL 8.0. return config.DefaultSQLMode } // MaxMemoryRows returns the maxMemoryRows flag value. -func (vc *vcursorImpl) MaxMemoryRows() int { - return maxMemoryRows +func (vc *VCursorImpl) MaxMemoryRows() int { + return vc.config.MaxMemoryRows } // ExceedsMaxMemoryRows returns a boolean indicating whether the maxMemoryRows value has been exceeded. // Returns false if the max memory rows override directive is set to true. -func (vc *vcursorImpl) ExceedsMaxMemoryRows(numRows int) bool { - return !vc.ignoreMaxMemoryRows && numRows > maxMemoryRows +func (vc *VCursorImpl) ExceedsMaxMemoryRows(numRows int) bool { + return !vc.ignoreMaxMemoryRows && numRows > vc.config.MaxMemoryRows } // SetIgnoreMaxMemoryRows sets the ignoreMaxMemoryRows value. -func (vc *vcursorImpl) SetIgnoreMaxMemoryRows(ignoreMaxMemoryRows bool) { +func (vc *VCursorImpl) SetIgnoreMaxMemoryRows(ignoreMaxMemoryRows bool) { vc.ignoreMaxMemoryRows = ignoreMaxMemoryRows } // RecordWarning stores the given warning in the current session -func (vc *vcursorImpl) RecordWarning(warning *querypb.QueryWarning) { - vc.safeSession.RecordWarning(warning) +func (vc *VCursorImpl) RecordWarning(warning *querypb.QueryWarning) { + vc.SafeSession.RecordWarning(warning) } // IsShardRoutingEnabled implements the VCursor interface. -func (vc *vcursorImpl) IsShardRoutingEnabled() bool { - return enableShardRouting +func (vc *VCursorImpl) IsShardRoutingEnabled() bool { + return vc.config.EnableShardRouting } -func (vc *vcursorImpl) ReadTransaction(ctx context.Context, transactionID string) (*querypb.TransactionMetadata, error) { +func (vc *VCursorImpl) ReadTransaction(ctx context.Context, transactionID string) (*querypb.TransactionMetadata, error) { return vc.executor.ReadTransaction(ctx, transactionID) } // UnresolvedTransactions gets the unresolved transactions for the given keyspace. If the keyspace is not given, // then we use the default keyspace. -func (vc *vcursorImpl) UnresolvedTransactions(ctx context.Context, keyspace string) ([]*querypb.TransactionMetadata, error) { +func (vc *VCursorImpl) UnresolvedTransactions(ctx context.Context, keyspace string) ([]*querypb.TransactionMetadata, error) { if keyspace == "" { keyspace = vc.GetKeyspace() } @@ -287,7 +375,7 @@ func (vc *vcursorImpl) UnresolvedTransactions(ctx context.Context, keyspace stri return vc.executor.UnresolvedTransactions(ctx, targets) } -func (vc *vcursorImpl) StartPrimitiveTrace() func() engine.Stats { +func (vc *VCursorImpl) StartPrimitiveTrace() func() engine.Stats { vc.interOpStats = make(map[engine.Primitive]engine.RowsReceived) vc.shardsStats = make(map[engine.Primitive]engine.ShardsQueried) return func() engine.Stats { @@ -300,8 +388,8 @@ func (vc *vcursorImpl) StartPrimitiveTrace() func() engine.Stats { // FindTable finds the specified table. If the keyspace what specified in the input, it gets used as qualifier. // Otherwise, the keyspace from the request is used, if one was provided. -func (vc *vcursorImpl) FindTable(name sqlparser.TableName) (*vindexes.Table, string, topodatapb.TabletType, key.Destination, error) { - destKeyspace, destTabletType, dest, err := vc.executor.ParseDestinationTarget(name.Qualifier.String()) +func (vc *VCursorImpl) FindTable(name sqlparser.TableName) (*vindexes.Table, string, topodatapb.TabletType, key.Destination, error) { + destKeyspace, destTabletType, dest, err := vc.ParseDestinationTarget(name.Qualifier.String()) if err != nil { return nil, "", destTabletType, nil, err } @@ -315,8 +403,8 @@ func (vc *vcursorImpl) FindTable(name sqlparser.TableName) (*vindexes.Table, str return table, destKeyspace, destTabletType, dest, err } -func (vc *vcursorImpl) FindView(name sqlparser.TableName) sqlparser.SelectStatement { - ks, _, _, err := vc.executor.ParseDestinationTarget(name.Qualifier.String()) +func (vc *VCursorImpl) FindView(name sqlparser.TableName) sqlparser.SelectStatement { + ks, _, _, err := vc.ParseDestinationTarget(name.Qualifier.String()) if err != nil { return nil } @@ -326,8 +414,8 @@ func (vc *vcursorImpl) FindView(name sqlparser.TableName) sqlparser.SelectStatem return vc.vschema.FindView(ks, name.Name.String()) } -func (vc *vcursorImpl) FindRoutedTable(name sqlparser.TableName) (*vindexes.Table, error) { - destKeyspace, destTabletType, _, err := vc.executor.ParseDestinationTarget(name.Qualifier.String()) +func (vc *VCursorImpl) FindRoutedTable(name sqlparser.TableName) (*vindexes.Table, error) { + destKeyspace, destTabletType, _, err := vc.ParseDestinationTarget(name.Qualifier.String()) if err != nil { return nil, err } @@ -344,14 +432,14 @@ func (vc *vcursorImpl) FindRoutedTable(name sqlparser.TableName) (*vindexes.Tabl } // FindTableOrVindex finds the specified table or vindex. -func (vc *vcursorImpl) FindTableOrVindex(name sqlparser.TableName) (*vindexes.Table, vindexes.Vindex, string, topodatapb.TabletType, key.Destination, error) { +func (vc *VCursorImpl) FindTableOrVindex(name sqlparser.TableName) (*vindexes.Table, vindexes.Vindex, string, topodatapb.TabletType, key.Destination, error) { if name.Qualifier.IsEmpty() && name.Name.String() == "dual" { // The magical MySQL dual table should only be resolved // when it is not qualified by a database name. return vc.getDualTable() } - destKeyspace, destTabletType, dest, err := vc.executor.ParseDestinationTarget(name.Qualifier.String()) + destKeyspace, destTabletType, dest, err := ParseDestinationTarget(name.Qualifier.String(), vc.tabletType, vc.vschema) if err != nil { return nil, nil, "", destTabletType, nil, err } @@ -365,7 +453,23 @@ func (vc *vcursorImpl) FindTableOrVindex(name sqlparser.TableName) (*vindexes.Ta return table, vindex, destKeyspace, destTabletType, dest, nil } -func (vc *vcursorImpl) getDualTable() (*vindexes.Table, vindexes.Vindex, string, topodatapb.TabletType, key.Destination, error) { +func (vc *VCursorImpl) ParseDestinationTarget(targetString string) (string, topodatapb.TabletType, key.Destination, error) { + return ParseDestinationTarget(targetString, vc.tabletType, vc.vschema) +} + +// ParseDestinationTarget parses destination target string and provides a keyspace if possible. +func ParseDestinationTarget(targetString string, tablet topodatapb.TabletType, vschema *vindexes.VSchema) (string, topodatapb.TabletType, key.Destination, error) { + destKeyspace, destTabletType, dest, err := topoprotopb.ParseDestination(targetString, tablet) + // If the keyspace is not specified, and there is only one keyspace in the VSchema, use that. + if destKeyspace == "" && len(vschema.Keyspaces) == 1 { + for k := range vschema.Keyspaces { + destKeyspace = k + } + } + return destKeyspace, destTabletType, dest, err +} + +func (vc *VCursorImpl) getDualTable() (*vindexes.Table, vindexes.Vindex, string, topodatapb.TabletType, key.Destination, error) { ksName := vc.getActualKeyspace() var ks *vindexes.Keyspace if ksName == "" { @@ -382,7 +486,7 @@ func (vc *vcursorImpl) getDualTable() (*vindexes.Table, vindexes.Vindex, string, return tbl, nil, ksName, topodatapb.TabletType_PRIMARY, nil, nil } -func (vc *vcursorImpl) getActualKeyspace() string { +func (vc *VCursorImpl) getActualKeyspace() string { if !sqlparser.SystemSchema(vc.keyspace) { return vc.keyspace } @@ -393,12 +497,12 @@ func (vc *vcursorImpl) getActualKeyspace() string { return ks.Name } -// DefaultKeyspace returns the default keyspace of the current request +// SelectedKeyspace returns the selected keyspace of the current request // if there is one. If the keyspace specified in the target cannot be // identified, it returns an error. -func (vc *vcursorImpl) DefaultKeyspace() (*vindexes.Keyspace, error) { +func (vc *VCursorImpl) SelectedKeyspace() (*vindexes.Keyspace, error) { if ignoreKeyspace(vc.keyspace) { - return nil, errNoKeyspace + return nil, ErrNoKeyspace } ks, ok := vc.vschema.Keyspaces[vc.keyspace] if !ok { @@ -409,12 +513,12 @@ func (vc *vcursorImpl) DefaultKeyspace() (*vindexes.Keyspace, error) { var errNoDbAvailable = vterrors.NewErrorf(vtrpcpb.Code_FAILED_PRECONDITION, vterrors.NoDB, "no database available") -func (vc *vcursorImpl) AnyKeyspace() (*vindexes.Keyspace, error) { - keyspace, err := vc.DefaultKeyspace() +func (vc *VCursorImpl) AnyKeyspace() (*vindexes.Keyspace, error) { + keyspace, err := vc.SelectedKeyspace() if err == nil { return keyspace, nil } - if err != errNoKeyspace { + if err != ErrNoKeyspace { return nil, err } @@ -435,7 +539,7 @@ func (vc *vcursorImpl) AnyKeyspace() (*vindexes.Keyspace, error) { } // getSortedServingKeyspaces gets the sorted serving keyspaces -func (vc *vcursorImpl) getSortedServingKeyspaces() []*vindexes.Keyspace { +func (vc *VCursorImpl) getSortedServingKeyspaces() []*vindexes.Keyspace { var keyspaces []*vindexes.Keyspace if vc.resolver != nil && vc.resolver.GetGateway() != nil { @@ -459,7 +563,7 @@ func (vc *vcursorImpl) getSortedServingKeyspaces() []*vindexes.Keyspace { return keyspaces } -func (vc *vcursorImpl) FirstSortedKeyspace() (*vindexes.Keyspace, error) { +func (vc *VCursorImpl) FirstSortedKeyspace() (*vindexes.Keyspace, error) { if len(vc.vschema.Keyspaces) == 0 { return nil, errNoDbAvailable } @@ -469,17 +573,17 @@ func (vc *vcursorImpl) FirstSortedKeyspace() (*vindexes.Keyspace, error) { } // SysVarSetEnabled implements the ContextVSchema interface -func (vc *vcursorImpl) SysVarSetEnabled() bool { +func (vc *VCursorImpl) SysVarSetEnabled() bool { return vc.GetSessionEnableSystemSettings() } // KeyspaceExists provides whether the keyspace exists or not. -func (vc *vcursorImpl) KeyspaceExists(ks string) bool { +func (vc *VCursorImpl) KeyspaceExists(ks string) bool { return vc.vschema.Keyspaces[ks] != nil } // AllKeyspace implements the ContextVSchema interface -func (vc *vcursorImpl) AllKeyspace() ([]*vindexes.Keyspace, error) { +func (vc *VCursorImpl) AllKeyspace() ([]*vindexes.Keyspace, error) { if len(vc.vschema.Keyspaces) == 0 { return nil, errNoDbAvailable } @@ -491,7 +595,7 @@ func (vc *vcursorImpl) AllKeyspace() ([]*vindexes.Keyspace, error) { } // FindKeyspace implements the VSchema interface -func (vc *vcursorImpl) FindKeyspace(keyspace string) (*vindexes.Keyspace, error) { +func (vc *VCursorImpl) FindKeyspace(keyspace string) (*vindexes.Keyspace, error) { if len(vc.vschema.Keyspaces) == 0 { return nil, errNoDbAvailable } @@ -504,28 +608,28 @@ func (vc *vcursorImpl) FindKeyspace(keyspace string) (*vindexes.Keyspace, error) } // Planner implements the ContextVSchema interface -func (vc *vcursorImpl) Planner() plancontext.PlannerVersion { - if vc.safeSession.Options != nil && - vc.safeSession.Options.PlannerVersion != querypb.ExecuteOptions_DEFAULT_PLANNER { - return vc.safeSession.Options.PlannerVersion +func (vc *VCursorImpl) Planner() plancontext.PlannerVersion { + if vc.SafeSession.Options != nil && + vc.SafeSession.Options.PlannerVersion != querypb.ExecuteOptions_DEFAULT_PLANNER { + return vc.SafeSession.Options.PlannerVersion } - return vc.pv + return vc.config.PlannerVersion } // GetSemTable implements the ContextVSchema interface -func (vc *vcursorImpl) GetSemTable() *semantics.SemTable { +func (vc *VCursorImpl) GetSemTable() *semantics.SemTable { return vc.semTable } // TargetString returns the current TargetString of the session. -func (vc *vcursorImpl) TargetString() string { - return vc.safeSession.TargetString +func (vc *VCursorImpl) TargetString() string { + return vc.SafeSession.TargetString } // MaxBufferingRetries is to represent max retries on buffering. const MaxBufferingRetries = 3 -func (vc *vcursorImpl) ExecutePrimitive(ctx context.Context, primitive engine.Primitive, bindVars map[string]*querypb.BindVariable, wantfields bool) (*sqltypes.Result, error) { +func (vc *VCursorImpl) ExecutePrimitive(ctx context.Context, primitive engine.Primitive, bindVars map[string]*querypb.BindVariable, wantfields bool) (*sqltypes.Result, error) { for try := 0; try < MaxBufferingRetries; try++ { res, err := primitive.TryExecute(ctx, vc, bindVars, wantfields) if err != nil && vterrors.RootCause(err) == buffer.ShardMissingError { @@ -537,7 +641,7 @@ func (vc *vcursorImpl) ExecutePrimitive(ctx context.Context, primitive engine.Pr return nil, vterrors.New(vtrpcpb.Code_UNAVAILABLE, "upstream shards are not available") } -func (vc *vcursorImpl) logOpTraffic(primitive engine.Primitive, res *sqltypes.Result) { +func (vc *VCursorImpl) logOpTraffic(primitive engine.Primitive, res *sqltypes.Result) { if vc.interOpStats != nil { rows := vc.interOpStats[primitive] if res == nil { @@ -549,14 +653,14 @@ func (vc *vcursorImpl) logOpTraffic(primitive engine.Primitive, res *sqltypes.Re } } -func (vc *vcursorImpl) logShardsQueried(primitive engine.Primitive, shardsNb int) { +func (vc *VCursorImpl) logShardsQueried(primitive engine.Primitive, shardsNb int) { if vc.shardsStats != nil { vc.shardsStats[primitive] += engine.ShardsQueried(shardsNb) } } -func (vc *vcursorImpl) ExecutePrimitiveStandalone(ctx context.Context, primitive engine.Primitive, bindVars map[string]*querypb.BindVariable, wantfields bool) (*sqltypes.Result, error) { - // clone the vcursorImpl with a new session. +func (vc *VCursorImpl) ExecutePrimitiveStandalone(ctx context.Context, primitive engine.Primitive, bindVars map[string]*querypb.BindVariable, wantfields bool) (*sqltypes.Result, error) { + // clone the VCursorImpl with a new session. newVC := vc.cloneWithAutocommitSession() for try := 0; try < MaxBufferingRetries; try++ { res, err := primitive.TryExecute(ctx, newVC, bindVars, wantfields) @@ -569,7 +673,7 @@ func (vc *vcursorImpl) ExecutePrimitiveStandalone(ctx context.Context, primitive return nil, vterrors.New(vtrpcpb.Code_UNAVAILABLE, "upstream shards are not available") } -func (vc *vcursorImpl) wrapCallback(callback func(*sqltypes.Result) error, primitive engine.Primitive) func(*sqltypes.Result) error { +func (vc *VCursorImpl) wrapCallback(callback func(*sqltypes.Result) error, primitive engine.Primitive) func(*sqltypes.Result) error { if vc.interOpStats == nil { return callback } @@ -580,7 +684,7 @@ func (vc *vcursorImpl) wrapCallback(callback func(*sqltypes.Result) error, primi } } -func (vc *vcursorImpl) StreamExecutePrimitive(ctx context.Context, primitive engine.Primitive, bindVars map[string]*querypb.BindVariable, wantfields bool, callback func(*sqltypes.Result) error) error { +func (vc *VCursorImpl) StreamExecutePrimitive(ctx context.Context, primitive engine.Primitive, bindVars map[string]*querypb.BindVariable, wantfields bool, callback func(*sqltypes.Result) error) error { callback = vc.wrapCallback(callback, primitive) for try := 0; try < MaxBufferingRetries; try++ { @@ -593,10 +697,10 @@ func (vc *vcursorImpl) StreamExecutePrimitive(ctx context.Context, primitive eng return vterrors.New(vtrpcpb.Code_UNAVAILABLE, "upstream shards are not available") } -func (vc *vcursorImpl) StreamExecutePrimitiveStandalone(ctx context.Context, primitive engine.Primitive, bindVars map[string]*querypb.BindVariable, wantfields bool, callback func(result *sqltypes.Result) error) error { +func (vc *VCursorImpl) StreamExecutePrimitiveStandalone(ctx context.Context, primitive engine.Primitive, bindVars map[string]*querypb.BindVariable, wantfields bool, callback func(result *sqltypes.Result) error) error { callback = vc.wrapCallback(callback, primitive) - // clone the vcursorImpl with a new session. + // clone the VCursorImpl with a new session. newVC := vc.cloneWithAutocommitSession() for try := 0; try < MaxBufferingRetries; try++ { err := primitive.TryStreamExecute(ctx, newVC, bindVars, wantfields, callback) @@ -609,12 +713,11 @@ func (vc *vcursorImpl) StreamExecutePrimitiveStandalone(ctx context.Context, pri } // Execute is part of the engine.VCursor interface. -func (vc *vcursorImpl) Execute(ctx context.Context, method string, query string, bindVars map[string]*querypb.BindVariable, rollbackOnError bool, co vtgatepb.CommitOrder) (*sqltypes.Result, error) { - session := vc.safeSession +func (vc *VCursorImpl) Execute(ctx context.Context, method string, query string, bindVars map[string]*querypb.BindVariable, rollbackOnError bool, co vtgatepb.CommitOrder) (*sqltypes.Result, error) { + session := vc.SafeSession if co == vtgatepb.CommitOrder_AUTOCOMMIT { // For autocommit, we have to create an independent session. - session = NewAutocommitSession(vc.safeSession.Session) - session.logging = vc.safeSession.logging + session = vc.SafeSession.NewAutocommitSession() rollbackOnError = false } else { session.SetCommitOrder(co) @@ -635,24 +738,22 @@ func (vc *vcursorImpl) Execute(ctx context.Context, method string, query string, // markSavepoint opens an internal savepoint before executing the original query. // This happens only when rollback is allowed and no other savepoint was executed // and the query is executed in an explicit transaction (i.e. started by the client). -func (vc *vcursorImpl) markSavepoint(ctx context.Context, needsRollbackOnParialExec bool, bindVars map[string]*querypb.BindVariable) error { - if !needsRollbackOnParialExec || !vc.safeSession.CanAddSavepoint() { +func (vc *VCursorImpl) markSavepoint(ctx context.Context, needsRollbackOnParialExec bool, bindVars map[string]*querypb.BindVariable) error { + if !needsRollbackOnParialExec || !vc.SafeSession.CanAddSavepoint() { return nil } uID := fmt.Sprintf("_vt%s", strings.ReplaceAll(uuid.NewString(), "-", "_")) spQuery := fmt.Sprintf("%ssavepoint %s%s", vc.marginComments.Leading, uID, vc.marginComments.Trailing) - _, err := vc.executor.Execute(ctx, nil, "MarkSavepoint", vc.safeSession, spQuery, bindVars) + _, err := vc.executor.Execute(ctx, nil, "MarkSavepoint", vc.SafeSession, spQuery, bindVars) if err != nil { return err } - vc.safeSession.SetSavepoint(uID) + vc.SafeSession.SetSavepoint(uID) return nil } -const txRollback = "Rollback Transaction" - // ExecuteMultiShard is part of the engine.VCursor interface. -func (vc *vcursorImpl) ExecuteMultiShard(ctx context.Context, primitive engine.Primitive, rss []*srvtopo.ResolvedShard, queries []*querypb.BoundQuery, rollbackOnError, canAutocommit bool) (*sqltypes.Result, []error) { +func (vc *VCursorImpl) ExecuteMultiShard(ctx context.Context, primitive engine.Primitive, rss []*srvtopo.ResolvedShard, queries []*querypb.BoundQuery, rollbackOnError, canAutocommit bool) (*sqltypes.Result, []error) { noOfShards := len(rss) atomic.AddUint64(&vc.logStats.ShardQueries, uint64(noOfShards)) err := vc.markSavepoint(ctx, rollbackOnError && (noOfShards > 1), map[string]*querypb.BindVariable{}) @@ -660,14 +761,14 @@ func (vc *vcursorImpl) ExecuteMultiShard(ctx context.Context, primitive engine.P return nil, []error{err} } - qr, errs := vc.executor.ExecuteMultiShard(ctx, primitive, rss, commentedShardQueries(queries, vc.marginComments), vc.safeSession, canAutocommit, vc.ignoreMaxMemoryRows, vc.resultsObserver) + qr, errs := vc.executor.ExecuteMultiShard(ctx, primitive, rss, commentedShardQueries(queries, vc.marginComments), vc.SafeSession, canAutocommit, vc.ignoreMaxMemoryRows, vc.observer) vc.setRollbackOnPartialExecIfRequired(len(errs) != len(rss), rollbackOnError) vc.logShardsQueried(primitive, len(rss)) return qr, errs } // StreamExecuteMulti is the streaming version of ExecuteMultiShard. -func (vc *vcursorImpl) StreamExecuteMulti(ctx context.Context, primitive engine.Primitive, query string, rss []*srvtopo.ResolvedShard, bindVars []map[string]*querypb.BindVariable, rollbackOnError bool, autocommit bool, callback func(reply *sqltypes.Result) error) []error { +func (vc *VCursorImpl) StreamExecuteMulti(ctx context.Context, primitive engine.Primitive, query string, rss []*srvtopo.ResolvedShard, bindVars []map[string]*querypb.BindVariable, rollbackOnError bool, autocommit bool, callback func(reply *sqltypes.Result) error) []error { callback = vc.wrapCallback(callback, primitive) noOfShards := len(rss) @@ -677,20 +778,20 @@ func (vc *vcursorImpl) StreamExecuteMulti(ctx context.Context, primitive engine. return []error{err} } - errs := vc.executor.StreamExecuteMulti(ctx, primitive, vc.marginComments.Leading+query+vc.marginComments.Trailing, rss, bindVars, vc.safeSession, autocommit, callback, vc.resultsObserver) + errs := vc.executor.StreamExecuteMulti(ctx, primitive, vc.marginComments.Leading+query+vc.marginComments.Trailing, rss, bindVars, vc.SafeSession, autocommit, callback, vc.observer) vc.setRollbackOnPartialExecIfRequired(len(errs) != len(rss), rollbackOnError) return errs } // ExecuteLock is for executing advisory lock statements. -func (vc *vcursorImpl) ExecuteLock(ctx context.Context, rs *srvtopo.ResolvedShard, query *querypb.BoundQuery, lockFuncType sqlparser.LockingFuncType) (*sqltypes.Result, error) { +func (vc *VCursorImpl) ExecuteLock(ctx context.Context, rs *srvtopo.ResolvedShard, query *querypb.BoundQuery, lockFuncType sqlparser.LockingFuncType) (*sqltypes.Result, error) { query.Sql = vc.marginComments.Leading + query.Sql + vc.marginComments.Trailing - return vc.executor.ExecuteLock(ctx, rs, query, vc.safeSession, lockFuncType) + return vc.executor.ExecuteLock(ctx, rs, query, vc.SafeSession, lockFuncType) } // ExecuteStandalone is part of the engine.VCursor interface. -func (vc *vcursorImpl) ExecuteStandalone(ctx context.Context, primitive engine.Primitive, query string, bindVars map[string]*querypb.BindVariable, rs *srvtopo.ResolvedShard) (*sqltypes.Result, error) { +func (vc *VCursorImpl) ExecuteStandalone(ctx context.Context, primitive engine.Primitive, query string, bindVars map[string]*querypb.BindVariable, rs *srvtopo.ResolvedShard) (*sqltypes.Result, error) { rss := []*srvtopo.ResolvedShard{rs} bqs := []*querypb.BoundQuery{ { @@ -700,13 +801,13 @@ func (vc *vcursorImpl) ExecuteStandalone(ctx context.Context, primitive engine.P } // The autocommit flag is always set to false because we currently don't // execute DMLs through ExecuteStandalone. - qr, errs := vc.executor.ExecuteMultiShard(ctx, primitive, rss, bqs, NewAutocommitSession(vc.safeSession.Session), false /* autocommit */, vc.ignoreMaxMemoryRows, vc.resultsObserver) + qr, errs := vc.executor.ExecuteMultiShard(ctx, primitive, rss, bqs, NewAutocommitSession(vc.SafeSession.Session), false /* autocommit */, vc.ignoreMaxMemoryRows, vc.observer) vc.logShardsQueried(primitive, len(rss)) return qr, vterrors.Aggregate(errs) } // ExecuteKeyspaceID is part of the engine.VCursor interface. -func (vc *vcursorImpl) ExecuteKeyspaceID(ctx context.Context, keyspace string, ksid []byte, query string, bindVars map[string]*querypb.BindVariable, rollbackOnError, autocommit bool) (*sqltypes.Result, error) { +func (vc *VCursorImpl) ExecuteKeyspaceID(ctx context.Context, keyspace string, ksid []byte, query string, bindVars map[string]*querypb.BindVariable, rollbackOnError, autocommit bool) (*sqltypes.Result, error) { atomic.AddUint64(&vc.logStats.ShardQueries, 1) rss, _, err := vc.ResolveDestinations(ctx, keyspace, nil, []key.Destination{key.DestinationKeyspaceID(ksid)}) if err != nil { @@ -723,17 +824,17 @@ func (vc *vcursorImpl) ExecuteKeyspaceID(ctx context.Context, keyspace string, k // This creates a transaction but that transaction is for locking purpose only and should not cause multi-db transaction error. // This fields helps in to ignore multi-db transaction error when it states `queryFromVindex`. if !rollbackOnError { - vc.safeSession.queryFromVindex = true + vc.SafeSession.SetQueryFromVindex(true) defer func() { - vc.safeSession.queryFromVindex = false + vc.SafeSession.SetQueryFromVindex(false) }() } qr, errs := vc.ExecuteMultiShard(ctx, nil, rss, queries, rollbackOnError, autocommit) return qr, vterrors.Aggregate(errs) } -func (vc *vcursorImpl) InTransactionAndIsDML() bool { - if !vc.safeSession.InTransaction() { +func (vc *VCursorImpl) InTransactionAndIsDML() bool { + if !vc.SafeSession.InTransaction() { return false } switch vc.logStats.StmtType { @@ -743,7 +844,7 @@ func (vc *vcursorImpl) InTransactionAndIsDML() bool { return false } -func (vc *vcursorImpl) LookupRowLockShardSession() vtgatepb.CommitOrder { +func (vc *VCursorImpl) LookupRowLockShardSession() vtgatepb.CommitOrder { switch vc.logStats.StmtType { case "DELETE", "UPDATE": return vtgatepb.CommitOrder_POST @@ -752,23 +853,23 @@ func (vc *vcursorImpl) LookupRowLockShardSession() vtgatepb.CommitOrder { } // AutocommitApproval is part of the engine.VCursor interface. -func (vc *vcursorImpl) AutocommitApproval() bool { - return vc.safeSession.AutocommitApproval() +func (vc *VCursorImpl) AutocommitApproval() bool { + return vc.SafeSession.AutocommitApproval() } // setRollbackOnPartialExecIfRequired sets the value on SafeSession.rollbackOnPartialExec // when the query gets successfully executed on at least one shard, // there does not exist any old savepoint for which rollback is already set // and rollback on error is allowed. -func (vc *vcursorImpl) setRollbackOnPartialExecIfRequired(atleastOneSuccess bool, rollbackOnError bool) { - if atleastOneSuccess && rollbackOnError && !vc.safeSession.IsRollbackSet() { - vc.safeSession.SetRollbackCommand() +func (vc *VCursorImpl) setRollbackOnPartialExecIfRequired(atleastOneSuccess bool, rollbackOnError bool) { + if atleastOneSuccess && rollbackOnError && !vc.SafeSession.IsRollbackSet() { + vc.SafeSession.SetRollbackCommand() } } // fixupPartiallyMovedShards checks if any of the shards in the route has a ShardRoutingRule (true when a keyspace // is in the middle of being moved to another keyspace using MoveTables moving a subset of shards at a time -func (vc *vcursorImpl) fixupPartiallyMovedShards(rss []*srvtopo.ResolvedShard) ([]*srvtopo.ResolvedShard, error) { +func (vc *VCursorImpl) fixupPartiallyMovedShards(rss []*srvtopo.ResolvedShard) ([]*srvtopo.ResolvedShard, error) { if vc.vschema.ShardRoutingRules == nil { return rss, nil } @@ -785,12 +886,12 @@ func (vc *vcursorImpl) fixupPartiallyMovedShards(rss []*srvtopo.ResolvedShard) ( return rss, nil } -func (vc *vcursorImpl) ResolveDestinations(ctx context.Context, keyspace string, ids []*querypb.Value, destinations []key.Destination) ([]*srvtopo.ResolvedShard, [][]*querypb.Value, error) { +func (vc *VCursorImpl) ResolveDestinations(ctx context.Context, keyspace string, ids []*querypb.Value, destinations []key.Destination) ([]*srvtopo.ResolvedShard, [][]*querypb.Value, error) { rss, values, err := vc.resolver.ResolveDestinations(ctx, keyspace, vc.tabletType, ids, destinations) if err != nil { return nil, nil, err } - if enableShardRouting { + if vc.config.EnableShardRouting { rss, err = vc.fixupPartiallyMovedShards(rss) if err != nil { return nil, nil, err @@ -799,12 +900,12 @@ func (vc *vcursorImpl) ResolveDestinations(ctx context.Context, keyspace string, return rss, values, err } -func (vc *vcursorImpl) ResolveDestinationsMultiCol(ctx context.Context, keyspace string, ids [][]sqltypes.Value, destinations []key.Destination) ([]*srvtopo.ResolvedShard, [][][]sqltypes.Value, error) { +func (vc *VCursorImpl) ResolveDestinationsMultiCol(ctx context.Context, keyspace string, ids [][]sqltypes.Value, destinations []key.Destination) ([]*srvtopo.ResolvedShard, [][][]sqltypes.Value, error) { rss, values, err := vc.resolver.ResolveDestinationsMultiCol(ctx, keyspace, vc.tabletType, ids, destinations) if err != nil { return nil, nil, err } - if enableShardRouting { + if vc.config.EnableShardRouting { rss, err = vc.fixupPartiallyMovedShards(rss) if err != nil { return nil, nil, err @@ -813,12 +914,12 @@ func (vc *vcursorImpl) ResolveDestinationsMultiCol(ctx context.Context, keyspace return rss, values, err } -func (vc *vcursorImpl) Session() engine.SessionActions { +func (vc *VCursorImpl) Session() engine.SessionActions { return vc } -func (vc *vcursorImpl) SetTarget(target string) error { - keyspace, tabletType, _, err := topoprotopb.ParseDestination(target, defaultTabletType) +func (vc *VCursorImpl) SetTarget(target string) error { + keyspace, tabletType, _, err := topoprotopb.ParseDestination(target, vc.config.DefaultTabletType) if err != nil { return err } @@ -826,10 +927,12 @@ func (vc *vcursorImpl) SetTarget(target string) error { return vterrors.VT05003(keyspace) } - if vc.safeSession.InTransaction() && tabletType != topodatapb.TabletType_PRIMARY { + if vc.SafeSession.InTransaction() && tabletType != topodatapb.TabletType_PRIMARY { return vterrors.NewErrorf(vtrpcpb.Code_INVALID_ARGUMENT, vterrors.LockOrActiveTransaction, "can't execute the given command because you have an active transaction") } - vc.safeSession.SetTargetString(target) + vc.SafeSession.SetTargetString(target) + vc.keyspace = keyspace + vc.tabletType = tabletType return nil } @@ -837,30 +940,30 @@ func ignoreKeyspace(keyspace string) bool { return keyspace == "" || sqlparser.SystemSchema(keyspace) } -func (vc *vcursorImpl) SetUDV(key string, value any) error { +func (vc *VCursorImpl) SetUDV(key string, value any) error { bindValue, err := sqltypes.BuildBindVariable(value) if err != nil { return err } - vc.safeSession.SetUserDefinedVariable(key, bindValue) + vc.SafeSession.SetUserDefinedVariable(key, bindValue) return nil } -func (vc *vcursorImpl) SetSysVar(name string, expr string) { - vc.safeSession.SetSystemVariable(name, expr) +func (vc *VCursorImpl) SetSysVar(name string, expr string) { + vc.SafeSession.SetSystemVariable(name, expr) } // NeedsReservedConn implements the SessionActions interface -func (vc *vcursorImpl) NeedsReservedConn() { - vc.safeSession.SetReservedConn(true) +func (vc *VCursorImpl) NeedsReservedConn() { + vc.SafeSession.SetReservedConn(true) } -func (vc *vcursorImpl) InReservedConn() bool { - return vc.safeSession.InReservedConn() +func (vc *VCursorImpl) InReservedConn() bool { + return vc.SafeSession.InReservedConn() } -func (vc *vcursorImpl) ShardSession() []*srvtopo.ResolvedShard { - ss := vc.safeSession.GetShardSessions() +func (vc *VCursorImpl) ShardSession() []*srvtopo.ResolvedShard { + ss := vc.SafeSession.GetShardSessions() if len(ss) == 0 { return nil } @@ -875,12 +978,12 @@ func (vc *vcursorImpl) ShardSession() []*srvtopo.ResolvedShard { } // Destination implements the ContextVSchema interface -func (vc *vcursorImpl) Destination() key.Destination { +func (vc *VCursorImpl) Destination() key.Destination { return vc.destination } // TabletType implements the ContextVSchema interface -func (vc *vcursorImpl) TabletType() topodatapb.TabletType { +func (vc *VCursorImpl) TabletType() topodatapb.TabletType { return vc.tabletType } @@ -899,13 +1002,13 @@ func commentedShardQueries(shardQueries []*querypb.BoundQuery, marginComments sq } // TargetDestination implements the ContextVSchema interface -func (vc *vcursorImpl) TargetDestination(qualifier string) (key.Destination, *vindexes.Keyspace, topodatapb.TabletType, error) { +func (vc *VCursorImpl) TargetDestination(qualifier string) (key.Destination, *vindexes.Keyspace, topodatapb.TabletType, error) { keyspaceName := vc.getActualKeyspace() if vc.destination == nil && qualifier != "" { keyspaceName = qualifier } if keyspaceName == "" { - return nil, nil, 0, errNoKeyspace + return nil, nil, 0, ErrNoKeyspace } keyspace := vc.vschema.Keyspaces[keyspaceName] if keyspace == nil { @@ -915,63 +1018,63 @@ func (vc *vcursorImpl) TargetDestination(qualifier string) (key.Destination, *vi } // SetAutocommit implements the SessionActions interface -func (vc *vcursorImpl) SetAutocommit(ctx context.Context, autocommit bool) error { - if autocommit && vc.safeSession.InTransaction() { - if err := vc.executor.Commit(ctx, vc.safeSession); err != nil { +func (vc *VCursorImpl) SetAutocommit(ctx context.Context, autocommit bool) error { + if autocommit && vc.SafeSession.InTransaction() { + if err := vc.executor.Commit(ctx, vc.SafeSession); err != nil { return err } } - vc.safeSession.Autocommit = autocommit + vc.SafeSession.Autocommit = autocommit return nil } // SetQueryTimeout implements the SessionActions interface -func (vc *vcursorImpl) SetQueryTimeout(maxExecutionTime int64) { - vc.safeSession.QueryTimeout = maxExecutionTime +func (vc *VCursorImpl) SetQueryTimeout(maxExecutionTime int64) { + vc.SafeSession.QueryTimeout = maxExecutionTime } // SetClientFoundRows implements the SessionActions interface -func (vc *vcursorImpl) SetClientFoundRows(_ context.Context, clientFoundRows bool) error { - vc.safeSession.GetOrCreateOptions().ClientFoundRows = clientFoundRows +func (vc *VCursorImpl) SetClientFoundRows(_ context.Context, clientFoundRows bool) error { + vc.SafeSession.GetOrCreateOptions().ClientFoundRows = clientFoundRows return nil } // SetSkipQueryPlanCache implements the SessionActions interface -func (vc *vcursorImpl) SetSkipQueryPlanCache(_ context.Context, skipQueryPlanCache bool) error { - vc.safeSession.GetOrCreateOptions().SkipQueryPlanCache = skipQueryPlanCache +func (vc *VCursorImpl) SetSkipQueryPlanCache(_ context.Context, skipQueryPlanCache bool) error { + vc.SafeSession.GetOrCreateOptions().SkipQueryPlanCache = skipQueryPlanCache return nil } // SetSQLSelectLimit implements the SessionActions interface -func (vc *vcursorImpl) SetSQLSelectLimit(limit int64) error { - vc.safeSession.GetOrCreateOptions().SqlSelectLimit = limit +func (vc *VCursorImpl) SetSQLSelectLimit(limit int64) error { + vc.SafeSession.GetOrCreateOptions().SqlSelectLimit = limit return nil } // SetTransactionMode implements the SessionActions interface -func (vc *vcursorImpl) SetTransactionMode(mode vtgatepb.TransactionMode) { - vc.safeSession.TransactionMode = mode +func (vc *VCursorImpl) SetTransactionMode(mode vtgatepb.TransactionMode) { + vc.SafeSession.TransactionMode = mode } // SetWorkload implements the SessionActions interface -func (vc *vcursorImpl) SetWorkload(workload querypb.ExecuteOptions_Workload) { - vc.safeSession.GetOrCreateOptions().Workload = workload +func (vc *VCursorImpl) SetWorkload(workload querypb.ExecuteOptions_Workload) { + vc.SafeSession.GetOrCreateOptions().Workload = workload } // SetPlannerVersion implements the SessionActions interface -func (vc *vcursorImpl) SetPlannerVersion(v plancontext.PlannerVersion) { - vc.safeSession.GetOrCreateOptions().PlannerVersion = v +func (vc *VCursorImpl) SetPlannerVersion(v plancontext.PlannerVersion) { + vc.SafeSession.GetOrCreateOptions().PlannerVersion = v } -func (vc *vcursorImpl) SetPriority(priority string) { +func (vc *VCursorImpl) SetPriority(priority string) { if priority != "" { - vc.safeSession.GetOrCreateOptions().Priority = priority - } else if vc.safeSession.Options != nil && vc.safeSession.Options.Priority != "" { - vc.safeSession.Options.Priority = "" + vc.SafeSession.GetOrCreateOptions().Priority = priority + } else if vc.SafeSession.Options != nil && vc.SafeSession.Options.Priority != "" { + vc.SafeSession.Options.Priority = "" } } -func (vc *vcursorImpl) SetExecQueryTimeout(timeout *int) { +func (vc *VCursorImpl) SetExecQueryTimeout(timeout *int) { // Determine the effective timeout: use passed timeout if non-nil, otherwise use session's query timeout if available var execTimeout *int if timeout != nil { @@ -982,153 +1085,152 @@ func (vc *vcursorImpl) SetExecQueryTimeout(timeout *int) { // If no effective timeout and no session options, return early if execTimeout == nil { - if vc.safeSession.GetOptions() == nil { + if vc.SafeSession.GetOptions() == nil { return } - vc.safeSession.GetOrCreateOptions().Timeout = nil + vc.SafeSession.GetOrCreateOptions().Timeout = nil return } vc.queryTimeout = time.Duration(*execTimeout) * time.Millisecond // Set the authoritative timeout using the determined execTimeout - vc.safeSession.GetOrCreateOptions().Timeout = &querypb.ExecuteOptions_AuthoritativeTimeout{ + vc.SafeSession.GetOrCreateOptions().Timeout = &querypb.ExecuteOptions_AuthoritativeTimeout{ AuthoritativeTimeout: int64(*execTimeout), } } // getQueryTimeout returns timeout based on the priority // session setting > global default specified by a flag. -func (vc *vcursorImpl) getQueryTimeout() int { - sessionQueryTimeout := int(vc.safeSession.GetQueryTimeout()) +func (vc *VCursorImpl) getQueryTimeout() int { + sessionQueryTimeout := int(vc.SafeSession.GetQueryTimeout()) if sessionQueryTimeout != 0 { return sessionQueryTimeout } - return queryTimeout + return vc.config.QueryTimeout } // SetConsolidator implements the SessionActions interface -func (vc *vcursorImpl) SetConsolidator(consolidator querypb.ExecuteOptions_Consolidator) { +func (vc *VCursorImpl) SetConsolidator(consolidator querypb.ExecuteOptions_Consolidator) { // Avoid creating session Options when they do not yet exist and the // consolidator is unspecified. - if consolidator == querypb.ExecuteOptions_CONSOLIDATOR_UNSPECIFIED && vc.safeSession.GetOptions() == nil { + if consolidator == querypb.ExecuteOptions_CONSOLIDATOR_UNSPECIFIED && vc.SafeSession.GetOptions() == nil { return } - vc.safeSession.GetOrCreateOptions().Consolidator = consolidator + vc.SafeSession.GetOrCreateOptions().Consolidator = consolidator } -func (vc *vcursorImpl) SetWorkloadName(workloadName string) { +func (vc *VCursorImpl) SetWorkloadName(workloadName string) { if workloadName != "" { - vc.safeSession.GetOrCreateOptions().WorkloadName = workloadName + vc.SafeSession.GetOrCreateOptions().WorkloadName = workloadName } } // SetFoundRows implements the SessionActions interface -func (vc *vcursorImpl) SetFoundRows(foundRows uint64) { - vc.safeSession.FoundRows = foundRows - vc.safeSession.foundRowsHandled = true +func (vc *VCursorImpl) SetFoundRows(foundRows uint64) { + vc.SafeSession.SetFoundRows(foundRows) } // SetDDLStrategy implements the SessionActions interface -func (vc *vcursorImpl) SetDDLStrategy(strategy string) { - vc.safeSession.SetDDLStrategy(strategy) +func (vc *VCursorImpl) SetDDLStrategy(strategy string) { + vc.SafeSession.SetDDLStrategy(strategy) } // GetDDLStrategy implements the SessionActions interface -func (vc *vcursorImpl) GetDDLStrategy() string { - return vc.safeSession.GetDDLStrategy() +func (vc *VCursorImpl) GetDDLStrategy() string { + return vc.SafeSession.GetDDLStrategy() } // SetMigrationContext implements the SessionActions interface -func (vc *vcursorImpl) SetMigrationContext(migrationContext string) { - vc.safeSession.SetMigrationContext(migrationContext) +func (vc *VCursorImpl) SetMigrationContext(migrationContext string) { + vc.SafeSession.SetMigrationContext(migrationContext) } // GetMigrationContext implements the SessionActions interface -func (vc *vcursorImpl) GetMigrationContext() string { - return vc.safeSession.GetMigrationContext() +func (vc *VCursorImpl) GetMigrationContext() string { + return vc.SafeSession.GetMigrationContext() } // GetSessionUUID implements the SessionActions interface -func (vc *vcursorImpl) GetSessionUUID() string { - return vc.safeSession.GetSessionUUID() +func (vc *VCursorImpl) GetSessionUUID() string { + return vc.SafeSession.GetSessionUUID() } // SetSessionEnableSystemSettings implements the SessionActions interface -func (vc *vcursorImpl) SetSessionEnableSystemSettings(_ context.Context, allow bool) error { - vc.safeSession.SetSessionEnableSystemSettings(allow) +func (vc *VCursorImpl) SetSessionEnableSystemSettings(_ context.Context, allow bool) error { + vc.SafeSession.SetSessionEnableSystemSettings(allow) return nil } // GetSessionEnableSystemSettings implements the SessionActions interface -func (vc *vcursorImpl) GetSessionEnableSystemSettings() bool { - return vc.safeSession.GetSessionEnableSystemSettings() +func (vc *VCursorImpl) GetSessionEnableSystemSettings() bool { + return vc.SafeSession.GetSessionEnableSystemSettings() } // SetReadAfterWriteGTID implements the SessionActions interface -func (vc *vcursorImpl) SetReadAfterWriteGTID(vtgtid string) { - vc.safeSession.SetReadAfterWriteGTID(vtgtid) +func (vc *VCursorImpl) SetReadAfterWriteGTID(vtgtid string) { + vc.SafeSession.SetReadAfterWriteGTID(vtgtid) } // SetReadAfterWriteTimeout implements the SessionActions interface -func (vc *vcursorImpl) SetReadAfterWriteTimeout(timeout float64) { - vc.safeSession.SetReadAfterWriteTimeout(timeout) +func (vc *VCursorImpl) SetReadAfterWriteTimeout(timeout float64) { + vc.SafeSession.SetReadAfterWriteTimeout(timeout) } // SetSessionTrackGTIDs implements the SessionActions interface -func (vc *vcursorImpl) SetSessionTrackGTIDs(enable bool) { - vc.safeSession.SetSessionTrackGtids(enable) +func (vc *VCursorImpl) SetSessionTrackGTIDs(enable bool) { + vc.SafeSession.SetSessionTrackGtids(enable) } // HasCreatedTempTable implements the SessionActions interface -func (vc *vcursorImpl) HasCreatedTempTable() { - vc.safeSession.GetOrCreateOptions().HasCreatedTempTables = true +func (vc *VCursorImpl) HasCreatedTempTable() { + vc.SafeSession.GetOrCreateOptions().HasCreatedTempTables = true } // GetWarnings implements the SessionActions interface -func (vc *vcursorImpl) GetWarnings() []*querypb.QueryWarning { - return vc.safeSession.GetWarnings() +func (vc *VCursorImpl) GetWarnings() []*querypb.QueryWarning { + return vc.SafeSession.GetWarnings() } // AnyAdvisoryLockTaken implements the SessionActions interface -func (vc *vcursorImpl) AnyAdvisoryLockTaken() bool { - return vc.safeSession.HasAdvisoryLock() +func (vc *VCursorImpl) AnyAdvisoryLockTaken() bool { + return vc.SafeSession.HasAdvisoryLock() } // AddAdvisoryLock implements the SessionActions interface -func (vc *vcursorImpl) AddAdvisoryLock(name string) { - vc.safeSession.AddAdvisoryLock(name) +func (vc *VCursorImpl) AddAdvisoryLock(name string) { + vc.SafeSession.AddAdvisoryLock(name) } // RemoveAdvisoryLock implements the SessionActions interface -func (vc *vcursorImpl) RemoveAdvisoryLock(name string) { - vc.safeSession.RemoveAdvisoryLock(name) +func (vc *VCursorImpl) RemoveAdvisoryLock(name string) { + vc.SafeSession.RemoveAdvisoryLock(name) } -func (vc *vcursorImpl) SetCommitOrder(co vtgatepb.CommitOrder) { - vc.safeSession.SetCommitOrder(co) +func (vc *VCursorImpl) SetCommitOrder(co vtgatepb.CommitOrder) { + vc.SafeSession.SetCommitOrder(co) } -func (vc *vcursorImpl) InTransaction() bool { - return vc.safeSession.InTransaction() +func (vc *VCursorImpl) InTransaction() bool { + return vc.SafeSession.InTransaction() } -func (vc *vcursorImpl) Commit(ctx context.Context) error { - return vc.executor.Commit(ctx, vc.safeSession) +func (vc *VCursorImpl) Commit(ctx context.Context) error { + return vc.executor.Commit(ctx, vc.SafeSession) } // GetDBDDLPluginName implements the VCursor interface -func (vc *vcursorImpl) GetDBDDLPluginName() string { - return dbDDLPlugin +func (vc *VCursorImpl) GetDBDDLPluginName() string { + return vc.config.DBDDLPlugin } // KeyspaceAvailable implements the VCursor interface -func (vc *vcursorImpl) KeyspaceAvailable(ks string) bool { +func (vc *VCursorImpl) KeyspaceAvailable(ks string) bool { _, exists := vc.executor.VSchema().Keyspaces[ks] return exists } // ErrorIfShardedF implements the VCursor interface -func (vc *vcursorImpl) ErrorIfShardedF(ks *vindexes.Keyspace, warn, errFormat string, params ...any) error { +func (vc *VCursorImpl) ErrorIfShardedF(ks *vindexes.Keyspace, warn, errFormat string, params ...any) error { if ks.Sharded { return vterrors.Errorf(vtrpcpb.Code_UNIMPLEMENTED, errFormat, params...) } @@ -1137,19 +1239,25 @@ func (vc *vcursorImpl) ErrorIfShardedF(ks *vindexes.Keyspace, warn, errFormat st return nil } +func (vc *VCursorImpl) GetAndEmptyWarnings() []*querypb.QueryWarning { + w := vc.warnings + vc.warnings = nil + return w +} + // WarnUnshardedOnly implements the VCursor interface -func (vc *vcursorImpl) WarnUnshardedOnly(format string, params ...any) { - if vc.warnShardedOnly { +func (vc *VCursorImpl) WarnUnshardedOnly(format string, params ...any) { + if vc.config.WarnShardedOnly { vc.warnings = append(vc.warnings, &querypb.QueryWarning{ Code: uint32(sqlerror.ERNotSupportedYet), Message: fmt.Sprintf(format, params...), }) - warnings.Add("WarnUnshardedOnly", 1) + vc.executor.AddWarningCount("WarnUnshardedOnly", 1) } } // PlannerWarning implements the VCursor interface -func (vc *vcursorImpl) PlannerWarning(message string) { +func (vc *VCursorImpl) PlannerWarning(message string) { if message == "" { return } @@ -1160,8 +1268,8 @@ func (vc *vcursorImpl) PlannerWarning(message string) { } // ForeignKeyMode implements the VCursor interface -func (vc *vcursorImpl) ForeignKeyMode(keyspace string) (vschemapb.Keyspace_ForeignKeyMode, error) { - if strings.ToLower(foreignKeyMode) == "disallow" { +func (vc *VCursorImpl) ForeignKeyMode(keyspace string) (vschemapb.Keyspace_ForeignKeyMode, error) { + if vc.config.ForeignKeyMode == vschemapb.Keyspace_disallow { return vschemapb.Keyspace_disallow, nil } ks := vc.vschema.Keyspaces[keyspace] @@ -1171,7 +1279,7 @@ func (vc *vcursorImpl) ForeignKeyMode(keyspace string) (vschemapb.Keyspace_Forei return ks.ForeignKeyMode, nil } -func (vc *vcursorImpl) KeyspaceError(keyspace string) error { +func (vc *VCursorImpl) KeyspaceError(keyspace string) error { ks := vc.vschema.Keyspaces[keyspace] if ks == nil { return vterrors.VT14004(keyspace) @@ -1179,14 +1287,14 @@ func (vc *vcursorImpl) KeyspaceError(keyspace string) error { return ks.Error } -func (vc *vcursorImpl) GetAggregateUDFs() []string { +func (vc *VCursorImpl) GetAggregateUDFs() []string { return vc.vschema.GetAggregateUDFs() } // FindMirrorRule finds the mirror rule for the requested table name and // VSchema tablet type. -func (vc *vcursorImpl) FindMirrorRule(name sqlparser.TableName) (*vindexes.MirrorRule, error) { - destKeyspace, destTabletType, _, err := vc.executor.ParseDestinationTarget(name.Qualifier.String()) +func (vc *VCursorImpl) FindMirrorRule(name sqlparser.TableName) (*vindexes.MirrorRule, error) { + destKeyspace, destTabletType, _, err := vc.ParseDestinationTarget(name.Qualifier.String()) if err != nil { return nil, err } @@ -1200,23 +1308,11 @@ func (vc *vcursorImpl) FindMirrorRule(name sqlparser.TableName) (*vindexes.Mirro return mirrorRule, err } -// ParseDestinationTarget parses destination target string and sets default keyspace if possible. -func parseDestinationTarget(targetString string, vschema *vindexes.VSchema) (string, topodatapb.TabletType, key.Destination, error) { - destKeyspace, destTabletType, dest, err := topoprotopb.ParseDestination(targetString, defaultTabletType) - // Set default keyspace - if destKeyspace == "" && len(vschema.Keyspaces) == 1 { - for k := range vschema.Keyspaces { - destKeyspace = k - } - } - return destKeyspace, destTabletType, dest, err -} - -func (vc *vcursorImpl) keyForPlan(ctx context.Context, query string, buf io.StringWriter) { +func (vc *VCursorImpl) KeyForPlan(ctx context.Context, query string, buf io.StringWriter) { _, _ = buf.WriteString(vc.keyspace) _, _ = buf.WriteString(vindexes.TabletTypeSuffix[vc.tabletType]) _, _ = buf.WriteString("+Collate:") - _, _ = buf.WriteString(vc.Environment().CollationEnv().LookupName(vc.collation)) + _, _ = buf.WriteString(vc.Environment().CollationEnv().LookupName(vc.config.Collation)) if vc.destination != nil { switch vc.destination.(type) { @@ -1246,11 +1342,11 @@ func (vc *vcursorImpl) keyForPlan(ctx context.Context, query string, buf io.Stri _, _ = buf.WriteString(query) } -func (vc *vcursorImpl) GetKeyspace() string { +func (vc *VCursorImpl) GetKeyspace() string { return vc.keyspace } -func (vc *vcursorImpl) ExecuteVSchema(ctx context.Context, keyspace string, vschemaDDL *sqlparser.AlterVschema) error { +func (vc *VCursorImpl) ExecuteVSchema(ctx context.Context, keyspace string, vschemaDDL *sqlparser.AlterVschema) error { srvVschema := vc.vm.GetCurrentSrvVschema() if srvVschema == nil { return vterrors.Errorf(vtrpcpb.Code_INTERNAL, "vschema not loaded") @@ -1271,7 +1367,7 @@ func (vc *vcursorImpl) ExecuteVSchema(ctx context.Context, keyspace string, vsch ksName = keyspace } if ksName == "" { - return errNoKeyspace + return ErrNoKeyspace } ks := srvVschema.Keyspaces[ksName] @@ -1285,43 +1381,43 @@ func (vc *vcursorImpl) ExecuteVSchema(ctx context.Context, keyspace string, vsch return vc.vm.UpdateVSchema(ctx, ksName, srvVschema) } -func (vc *vcursorImpl) MessageStream(ctx context.Context, rss []*srvtopo.ResolvedShard, tableName string, callback func(*sqltypes.Result) error) error { +func (vc *VCursorImpl) MessageStream(ctx context.Context, rss []*srvtopo.ResolvedShard, tableName string, callback func(*sqltypes.Result) error) error { atomic.AddUint64(&vc.logStats.ShardQueries, uint64(len(rss))) return vc.executor.ExecuteMessageStream(ctx, rss, tableName, callback) } -func (vc *vcursorImpl) VStream(ctx context.Context, rss []*srvtopo.ResolvedShard, filter *binlogdatapb.Filter, gtid string, callback func(evs []*binlogdatapb.VEvent) error) error { +func (vc *VCursorImpl) VStream(ctx context.Context, rss []*srvtopo.ResolvedShard, filter *binlogdatapb.Filter, gtid string, callback func(evs []*binlogdatapb.VEvent) error) error { return vc.executor.ExecuteVStream(ctx, rss, filter, gtid, callback) } -func (vc *vcursorImpl) ShowExec(ctx context.Context, command sqlparser.ShowCommandType, filter *sqlparser.ShowFilter) (*sqltypes.Result, error) { +func (vc *VCursorImpl) ShowExec(ctx context.Context, command sqlparser.ShowCommandType, filter *sqlparser.ShowFilter) (*sqltypes.Result, error) { switch command { case sqlparser.VitessReplicationStatus: - return vc.executor.showVitessReplicationStatus(ctx, filter) + return vc.executor.ShowVitessReplicationStatus(ctx, filter) case sqlparser.VitessShards: - return vc.executor.showShards(ctx, filter, vc.tabletType) + return vc.executor.ShowShards(ctx, filter, vc.tabletType) case sqlparser.VitessTablets: - return vc.executor.showTablets(filter) + return vc.executor.ShowTablets(filter) case sqlparser.VitessVariables: - return vc.executor.showVitessMetadata(ctx, filter) + return vc.executor.ShowVitessMetadata(ctx, filter) default: return nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "bug: unexpected show command: %v", command) } } -func (vc *vcursorImpl) GetVSchema() *vindexes.VSchema { +func (vc *VCursorImpl) GetVSchema() *vindexes.VSchema { return vc.vschema } -func (vc *vcursorImpl) GetSrvVschema() *vschemapb.SrvVSchema { +func (vc *VCursorImpl) GetSrvVschema() *vschemapb.SrvVSchema { return vc.vm.GetCurrentSrvVschema() } -func (vc *vcursorImpl) SetExec(ctx context.Context, name string, value string) error { - return vc.executor.setVitessMetadata(ctx, name, value) +func (vc *VCursorImpl) SetExec(ctx context.Context, name string, value string) error { + return vc.executor.SetVitessMetadata(ctx, name, value) } -func (vc *vcursorImpl) ThrottleApp(ctx context.Context, throttledAppRule *topodatapb.ThrottledAppRule) (err error) { +func (vc *VCursorImpl) ThrottleApp(ctx context.Context, throttledAppRule *topodatapb.ThrottledAppRule) (err error) { if throttledAppRule == nil { return vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "ThrottleApp: nil rule") } @@ -1377,147 +1473,60 @@ func (vc *vcursorImpl) ThrottleApp(ctx context.Context, throttledAppRule *topoda return err } -func (vc *vcursorImpl) CanUseSetVar() bool { - return vc.Environment().Parser().IsMySQL80AndAbove() && setVarEnabled +func (vc *VCursorImpl) CanUseSetVar() bool { + return vc.Environment().Parser().IsMySQL80AndAbove() && vc.config.SetVarEnabled } -func (vc *vcursorImpl) ReleaseLock(ctx context.Context) error { - return vc.executor.ReleaseLock(ctx, vc.safeSession) +func (vc *VCursorImpl) ReleaseLock(ctx context.Context) error { + return vc.executor.ReleaseLock(ctx, vc.SafeSession) } -func (vc *vcursorImpl) cloneWithAutocommitSession() *vcursorImpl { - safeSession := NewAutocommitSession(vc.safeSession.Session) - safeSession.logging = vc.safeSession.logging - return &vcursorImpl{ - safeSession: safeSession, - keyspace: vc.keyspace, - tabletType: vc.tabletType, - destination: vc.destination, - marginComments: vc.marginComments, - executor: vc.executor, - logStats: vc.logStats, - collation: vc.collation, - resolver: vc.resolver, - vschema: vc.vschema, - vm: vc.vm, - topoServer: vc.topoServer, - warnShardedOnly: vc.warnShardedOnly, - pv: vc.pv, - resultsObserver: vc.resultsObserver, - } -} - -func (vc *vcursorImpl) VExplainLogging() { - vc.safeSession.EnableLogging(vc.Environment().Parser()) +func (vc *VCursorImpl) VExplainLogging() { + vc.SafeSession.EnableLogging(vc.Environment().Parser()) } -func (vc *vcursorImpl) GetVExplainLogs() []engine.ExecuteEntry { - return vc.safeSession.logging.GetLogs() +func (vc *VCursorImpl) GetVExplainLogs() []engine.ExecuteEntry { + return vc.SafeSession.GetLogs() } -func (vc *vcursorImpl) FindRoutedShard(keyspace, shard string) (keyspaceName string, err error) { +func (vc *VCursorImpl) FindRoutedShard(keyspace, shard string) (keyspaceName string, err error) { return vc.vschema.FindRoutedShard(keyspace, shard) } -func (vc *vcursorImpl) IsViewsEnabled() bool { - return enableViews -} - -func (vc *vcursorImpl) GetUDV(name string) *querypb.BindVariable { - return vc.safeSession.GetUDV(name) +func (vc *VCursorImpl) IsViewsEnabled() bool { + return vc.config.EnableViews } -func (vc *vcursorImpl) PlanPrepareStatement(ctx context.Context, query string) (*engine.Plan, sqlparser.Statement, error) { - return vc.executor.planPrepareStmt(ctx, vc, query) +func (vc *VCursorImpl) GetUDV(name string) *querypb.BindVariable { + return vc.SafeSession.GetUDV(name) } -func (vc *vcursorImpl) ClearPrepareData(name string) { - delete(vc.safeSession.PrepareStatement, name) +func (vc *VCursorImpl) PlanPrepareStatement(ctx context.Context, query string) (*engine.Plan, sqlparser.Statement, error) { + return vc.executor.PlanPrepareStmt(ctx, vc, query) } -func (vc *vcursorImpl) StorePrepareData(stmtName string, prepareData *vtgatepb.PrepareData) { - vc.safeSession.StorePrepareData(stmtName, prepareData) +func (vc *VCursorImpl) ClearPrepareData(name string) { + delete(vc.SafeSession.PrepareStatement, name) } -func (vc *vcursorImpl) GetPrepareData(stmtName string) *vtgatepb.PrepareData { - return vc.safeSession.GetPrepareData(stmtName) +func (vc *VCursorImpl) StorePrepareData(stmtName string, prepareData *vtgatepb.PrepareData) { + vc.SafeSession.StorePrepareData(stmtName, prepareData) } -func (vc *vcursorImpl) GetWarmingReadsPercent() int { - return vc.warmingReadsPercent +func (vc *VCursorImpl) GetPrepareData(stmtName string) *vtgatepb.PrepareData { + return vc.SafeSession.GetPrepareData(stmtName) } -func (vc *vcursorImpl) GetWarmingReadsChannel() chan bool { - return vc.warmingReadsChannel -} - -func (vc *vcursorImpl) CloneForReplicaWarming(ctx context.Context) engine.VCursor { - callerId := callerid.EffectiveCallerIDFromContext(ctx) - immediateCallerId := callerid.ImmediateCallerIDFromContext(ctx) - - timedCtx, _ := context.WithTimeout(context.Background(), warmingReadsQueryTimeout) // nolint - clonedCtx := callerid.NewContext(timedCtx, callerId, immediateCallerId) - - v := &vcursorImpl{ - safeSession: NewAutocommitSession(vc.safeSession.Session), - keyspace: vc.keyspace, - tabletType: topodatapb.TabletType_REPLICA, - destination: vc.destination, - marginComments: vc.marginComments, - executor: vc.executor, - resolver: vc.resolver, - topoServer: vc.topoServer, - logStats: &logstats.LogStats{Ctx: clonedCtx}, - collation: vc.collation, - ignoreMaxMemoryRows: vc.ignoreMaxMemoryRows, - vschema: vc.vschema, - vm: vc.vm, - semTable: vc.semTable, - warnShardedOnly: vc.warnShardedOnly, - warnings: vc.warnings, - pv: vc.pv, - resultsObserver: nullResultsObserver{}, - } - - v.marginComments.Trailing += "/* warming read */" - - return v +func (vc *VCursorImpl) GetWarmingReadsPercent() int { + return vc.config.WarmingReadsPercent } -func (vc *vcursorImpl) CloneForMirroring(ctx context.Context) engine.VCursor { - callerId := callerid.EffectiveCallerIDFromContext(ctx) - immediateCallerId := callerid.ImmediateCallerIDFromContext(ctx) - - clonedCtx := callerid.NewContext(ctx, callerId, immediateCallerId) - - v := &vcursorImpl{ - safeSession: NewAutocommitSession(vc.safeSession.Session), - keyspace: vc.keyspace, - tabletType: vc.tabletType, - destination: vc.destination, - marginComments: vc.marginComments, - executor: vc.executor, - resolver: vc.resolver, - topoServer: vc.topoServer, - logStats: &logstats.LogStats{Ctx: clonedCtx}, - collation: vc.collation, - ignoreMaxMemoryRows: vc.ignoreMaxMemoryRows, - vschema: vc.vschema, - vm: vc.vm, - semTable: vc.semTable, - warnShardedOnly: vc.warnShardedOnly, - warnings: vc.warnings, - pv: vc.pv, - resultsObserver: nullResultsObserver{}, - } - - v.marginComments.Trailing += "/* mirror query */" - - return v +func (vc *VCursorImpl) GetWarmingReadsChannel() chan bool { + return vc.config.WarmingReadsChannel } // UpdateForeignKeyChecksState updates the foreign key checks state of the vcursor. -func (vc *vcursorImpl) UpdateForeignKeyChecksState(fkStateFromQuery *bool) { +func (vc *VCursorImpl) UpdateForeignKeyChecksState(fkStateFromQuery *bool) { // Initialize the state to unspecified. vc.fkChecksState = nil // If the query has a SET_VAR optimizer hint that explicitly sets the foreign key checks state, @@ -1527,17 +1536,36 @@ func (vc *vcursorImpl) UpdateForeignKeyChecksState(fkStateFromQuery *bool) { return } // If the query doesn't have anything, then we consult the session state. - vc.fkChecksState = vc.safeSession.ForeignKeyChecks() + vc.fkChecksState = vc.SafeSession.ForeignKeyChecks() } // GetForeignKeyChecksState gets the stored foreign key checks state in the vcursor. -func (vc *vcursorImpl) GetForeignKeyChecksState() *bool { +func (vc *VCursorImpl) GetForeignKeyChecksState() *bool { return vc.fkChecksState } // RecordMirrorStats is used to record stats about a mirror query. -func (vc *vcursorImpl) RecordMirrorStats(sourceExecTime, targetExecTime time.Duration, targetErr error) { +func (vc *VCursorImpl) RecordMirrorStats(sourceExecTime, targetExecTime time.Duration, targetErr error) { vc.logStats.MirrorSourceExecuteTime = sourceExecTime vc.logStats.MirrorTargetExecuteTime = targetExecTime vc.logStats.MirrorTargetError = targetErr } + +func (vc *VCursorImpl) GetMarginComments() sqlparser.MarginComments { + return vc.marginComments +} + +func (vc *VCursorImpl) CachePlan() bool { + return vc.SafeSession.CachePlan() +} + +func (vc *VCursorImpl) GetContextWithTimeOut(ctx context.Context) (context.Context, context.CancelFunc) { + if vc.queryTimeout == 0 { + return ctx, func() {} + } + return context.WithTimeout(ctx, vc.queryTimeout) +} + +func (vc *VCursorImpl) IgnoreMaxMemoryRows() bool { + return vc.ignoreMaxMemoryRows +} diff --git a/go/vt/vtgate/vcursor_impl_test.go b/go/vt/vtgate/executorcontext/vcursor_impl_test.go similarity index 60% rename from go/vt/vtgate/vcursor_impl_test.go rename to go/vt/vtgate/executorcontext/vcursor_impl_test.go index 95d9a18078d..16d2c03bf1c 100644 --- a/go/vt/vtgate/vcursor_impl_test.go +++ b/go/vt/vtgate/executorcontext/vcursor_impl_test.go @@ -1,8 +1,23 @@ -package vtgate +/* +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 executorcontext import ( "context" - "encoding/hex" "errors" "fmt" "strconv" @@ -12,10 +27,16 @@ import ( "github.com/stretchr/testify/require" + "vitess.io/vitess/go/mysql/collations" + "vitess.io/vitess/go/sqltypes" + binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" + "vitess.io/vitess/go/vt/vtenv" + "vitess.io/vitess/go/vt/vtgate/engine" + "vitess.io/vitess/go/vt/vtgate/vtgateservice" + "vitess.io/vitess/go/vt/key" "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/srvtopo" - "vitess.io/vitess/go/vt/topo" "vitess.io/vitess/go/vt/vtgate/logstats" "vitess.io/vitess/go/vt/vtgate/vindexes" @@ -39,48 +60,6 @@ func (f fakeVSchemaOperator) UpdateVSchema(ctx context.Context, ksName string, v panic("implement me") } -type fakeTopoServer struct{} - -// GetTopoServer returns the full topo.Server instance. -func (f *fakeTopoServer) GetTopoServer() (*topo.Server, error) { - return nil, nil -} - -// GetSrvKeyspaceNames returns the list of keyspaces served in -// the provided cell. -func (f *fakeTopoServer) GetSrvKeyspaceNames(ctx context.Context, cell string, staleOK bool) ([]string, error) { - return []string{"ks1"}, nil -} - -// GetSrvKeyspace returns the SrvKeyspace for a cell/keyspace. -func (f *fakeTopoServer) GetSrvKeyspace(ctx context.Context, cell, keyspace string) (*topodatapb.SrvKeyspace, error) { - zeroHexBytes, _ := hex.DecodeString("") - eightyHexBytes, _ := hex.DecodeString("80") - ks := &topodatapb.SrvKeyspace{ - Partitions: []*topodatapb.SrvKeyspace_KeyspacePartition{ - { - ServedType: topodatapb.TabletType_PRIMARY, - ShardReferences: []*topodatapb.ShardReference{ - {Name: "-80", KeyRange: &topodatapb.KeyRange{Start: zeroHexBytes, End: eightyHexBytes}}, - {Name: "80-", KeyRange: &topodatapb.KeyRange{Start: eightyHexBytes, End: zeroHexBytes}}, - }, - }, - }, - } - return ks, nil -} - -func (f *fakeTopoServer) WatchSrvKeyspace(ctx context.Context, cell, keyspace string, callback func(*topodatapb.SrvKeyspace, error) bool) { - ks, err := f.GetSrvKeyspace(ctx, cell, keyspace) - callback(ks, err) -} - -// WatchSrvVSchema starts watching the SrvVSchema object for -// the provided cell. It will call the callback when -// a new value or an error occurs. -func (f *fakeTopoServer) WatchSrvVSchema(ctx context.Context, cell string, callback func(*vschemapb.SrvVSchema, error) bool) { -} - func TestDestinationKeyspace(t *testing.T) { ks1 := &vindexes.Keyspace{ Name: "ks1", @@ -184,13 +163,17 @@ func TestDestinationKeyspace(t *testing.T) { }, { vschema: vschemaWith2KS, targetString: "", - expectedError: errNoKeyspace.Error(), + expectedError: ErrNoKeyspace.Error(), }} - r, _, _, _, _ := createExecutorEnv(t) for i, tc := range tests { t.Run(strconv.Itoa(i)+tc.targetString, func(t *testing.T) { - impl, _ := newVCursorImpl(NewSafeSession(&vtgatepb.Session{TargetString: tc.targetString}), sqlparser.MarginComments{}, r, nil, &fakeVSchemaOperator{vschema: tc.vschema}, tc.vschema, nil, nil, false, querypb.ExecuteOptions_Gen4) + session := NewSafeSession(&vtgatepb.Session{TargetString: tc.targetString}) + impl, _ := NewVCursorImpl(session, sqlparser.MarginComments{}, nil, nil, + &fakeVSchemaOperator{vschema: tc.vschema}, tc.vschema, nil, nil, + fakeObserver{}, VCursorConfig{ + DefaultTabletType: topodatapb.TabletType_PRIMARY, + }) impl.vschema = tc.vschema dest, keyspace, tabletType, err := impl.TargetDestination(tc.qualifier) if tc.expectedError == "" { @@ -250,15 +233,15 @@ func TestSetTarget(t *testing.T) { expectedError: "can't execute the given command because you have an active transaction", }} - r, _, _, _, _ := createExecutorEnv(t) for i, tc := range tests { t.Run(fmt.Sprintf("%d#%s", i, tc.targetString), func(t *testing.T) { - vc, _ := newVCursorImpl(NewSafeSession(&vtgatepb.Session{InTransaction: true}), sqlparser.MarginComments{}, r, nil, &fakeVSchemaOperator{vschema: tc.vschema}, tc.vschema, nil, nil, false, querypb.ExecuteOptions_Gen4) + cfg := VCursorConfig{DefaultTabletType: topodatapb.TabletType_PRIMARY} + vc, _ := NewVCursorImpl(NewSafeSession(&vtgatepb.Session{InTransaction: true}), sqlparser.MarginComments{}, nil, nil, &fakeVSchemaOperator{vschema: tc.vschema}, tc.vschema, nil, nil, fakeObserver{}, cfg) vc.vschema = tc.vschema err := vc.SetTarget(tc.targetString) if tc.expectedError == "" { require.NoError(t, err) - require.Equal(t, vc.safeSession.TargetString, tc.targetString) + require.Equal(t, vc.SafeSession.TargetString, tc.targetString) } else { require.EqualError(t, err, tc.expectedError) } @@ -299,17 +282,20 @@ func TestKeyForPlan(t *testing.T) { expectedPlanPrefixKey: "ks1@replica+Collate:utf8mb4_0900_ai_ci+Query:SELECT 1", }} - r, _, _, _, _ := createExecutorEnv(t) for i, tc := range tests { t.Run(fmt.Sprintf("%d#%s", i, tc.targetString), func(t *testing.T) { ss := NewSafeSession(&vtgatepb.Session{InTransaction: false}) ss.SetTargetString(tc.targetString) - vc, err := newVCursorImpl(ss, sqlparser.MarginComments{}, r, nil, &fakeVSchemaOperator{vschema: tc.vschema}, tc.vschema, srvtopo.NewResolver(&fakeTopoServer{}, nil, ""), nil, false, querypb.ExecuteOptions_Gen4) + cfg := VCursorConfig{ + Collation: collations.CollationUtf8mb4ID, + DefaultTabletType: topodatapb.TabletType_PRIMARY, + } + vc, err := NewVCursorImpl(ss, sqlparser.MarginComments{}, &fakeExecutor{}, nil, &fakeVSchemaOperator{vschema: tc.vschema}, tc.vschema, srvtopo.NewResolver(&FakeTopoServer{}, nil, ""), nil, fakeObserver{}, cfg) require.NoError(t, err) vc.vschema = tc.vschema var buf strings.Builder - vc.keyForPlan(context.Background(), "SELECT 1", &buf) + vc.KeyForPlan(context.Background(), "SELECT 1", &buf) require.Equal(t, tc.expectedPlanPrefixKey, buf.String()) }) } @@ -327,8 +313,7 @@ func TestFirstSortedKeyspace(t *testing.T) { }, } - r, _, _, _, _ := createExecutorEnv(t) - vc, err := newVCursorImpl(NewSafeSession(nil), sqlparser.MarginComments{}, r, nil, &fakeVSchemaOperator{vschema: vschemaWith2KS}, vschemaWith2KS, srvtopo.NewResolver(&fakeTopoServer{}, nil, ""), nil, false, querypb.ExecuteOptions_Gen4) + vc, err := NewVCursorImpl(NewSafeSession(nil), sqlparser.MarginComments{}, nil, nil, &fakeVSchemaOperator{vschema: vschemaWith2KS}, vschemaWith2KS, srvtopo.NewResolver(&FakeTopoServer{}, nil, ""), nil, fakeObserver{}, VCursorConfig{}) require.NoError(t, err) ks, err := vc.FirstSortedKeyspace() require.NoError(t, err) @@ -338,13 +323,13 @@ func TestFirstSortedKeyspace(t *testing.T) { // TestSetExecQueryTimeout tests the SetExecQueryTimeout method. // Validates the timeout value is set based on override rule. func TestSetExecQueryTimeout(t *testing.T) { - executor, _, _, _, _ := createExecutorEnv(t) safeSession := NewSafeSession(nil) - vc, err := newVCursorImpl(safeSession, sqlparser.MarginComments{}, executor, nil, nil, &vindexes.VSchema{}, nil, nil, false, querypb.ExecuteOptions_Gen4) + vc, err := NewVCursorImpl(safeSession, sqlparser.MarginComments{}, nil, nil, nil, &vindexes.VSchema{}, nil, nil, fakeObserver{}, VCursorConfig{ + // flag timeout + QueryTimeout: 20, + }) require.NoError(t, err) - // flag timeout - queryTimeout = 20 vc.SetExecQueryTimeout(nil) require.Equal(t, 20*time.Millisecond, vc.queryTimeout) require.NotNil(t, safeSession.Options.Timeout) @@ -371,8 +356,8 @@ func TestSetExecQueryTimeout(t *testing.T) { require.NotNil(t, safeSession.Options.Timeout) require.EqualValues(t, 0, safeSession.Options.GetAuthoritativeTimeout()) - // reset - queryTimeout = 0 + // reset flag timeout + vc.config.QueryTimeout = 0 safeSession.SetQueryTimeout(0) vc.SetExecQueryTimeout(nil) require.Equal(t, 0*time.Millisecond, vc.queryTimeout) @@ -381,10 +366,9 @@ func TestSetExecQueryTimeout(t *testing.T) { } func TestRecordMirrorStats(t *testing.T) { - executor, _, _, _, _ := createExecutorEnv(t) safeSession := NewSafeSession(nil) logStats := logstats.NewLogStats(context.Background(), t.Name(), "select 1", "", nil) - vc, err := newVCursorImpl(safeSession, sqlparser.MarginComments{}, executor, logStats, nil, &vindexes.VSchema{}, nil, nil, false, querypb.ExecuteOptions_Gen4) + vc, err := NewVCursorImpl(safeSession, sqlparser.MarginComments{}, nil, logStats, nil, &vindexes.VSchema{}, nil, nil, fakeObserver{}, VCursorConfig{}) require.NoError(t, err) require.Zero(t, logStats.MirrorSourceExecuteTime) @@ -397,3 +381,113 @@ func TestRecordMirrorStats(t *testing.T) { require.Equal(t, 20*time.Millisecond, logStats.MirrorTargetExecuteTime) require.ErrorContains(t, logStats.MirrorTargetError, "test error") } + +type fakeExecutor struct{} + +func (f fakeExecutor) Execute(ctx context.Context, mysqlCtx vtgateservice.MySQLConnection, method string, session *SafeSession, s string, vars map[string]*querypb.BindVariable) (*sqltypes.Result, error) { + // TODO implement me + panic("implement me") +} + +func (f fakeExecutor) ExecuteMultiShard(ctx context.Context, primitive engine.Primitive, rss []*srvtopo.ResolvedShard, queries []*querypb.BoundQuery, session *SafeSession, autocommit bool, ignoreMaxMemoryRows bool, resultsObserver ResultsObserver) (qr *sqltypes.Result, errs []error) { + // TODO implement me + panic("implement me") +} + +func (f fakeExecutor) StreamExecuteMulti(ctx context.Context, primitive engine.Primitive, query string, rss []*srvtopo.ResolvedShard, vars []map[string]*querypb.BindVariable, session *SafeSession, autocommit bool, callback func(reply *sqltypes.Result) error, observer ResultsObserver) []error { + // TODO implement me + panic("implement me") +} + +func (f fakeExecutor) ExecuteLock(ctx context.Context, rs *srvtopo.ResolvedShard, query *querypb.BoundQuery, session *SafeSession, lockFuncType sqlparser.LockingFuncType) (*sqltypes.Result, error) { + // TODO implement me + panic("implement me") +} + +func (f fakeExecutor) Commit(ctx context.Context, safeSession *SafeSession) error { + // TODO implement me + panic("implement me") +} + +func (f fakeExecutor) ExecuteMessageStream(ctx context.Context, rss []*srvtopo.ResolvedShard, name string, callback func(*sqltypes.Result) error) error { + // TODO implement me + panic("implement me") +} + +func (f fakeExecutor) ExecuteVStream(ctx context.Context, rss []*srvtopo.ResolvedShard, filter *binlogdatapb.Filter, gtid string, callback func(evs []*binlogdatapb.VEvent) error) error { + // TODO implement me + panic("implement me") +} + +func (f fakeExecutor) ReleaseLock(ctx context.Context, session *SafeSession) error { + // TODO implement me + panic("implement me") +} + +func (f fakeExecutor) ShowVitessReplicationStatus(ctx context.Context, filter *sqlparser.ShowFilter) (*sqltypes.Result, error) { + // TODO implement me + panic("implement me") +} + +func (f fakeExecutor) ShowShards(ctx context.Context, filter *sqlparser.ShowFilter, destTabletType topodatapb.TabletType) (*sqltypes.Result, error) { + // TODO implement me + panic("implement me") +} + +func (f fakeExecutor) ShowTablets(filter *sqlparser.ShowFilter) (*sqltypes.Result, error) { + // TODO implement me + panic("implement me") +} + +func (f fakeExecutor) ShowVitessMetadata(ctx context.Context, filter *sqlparser.ShowFilter) (*sqltypes.Result, error) { + // TODO implement me + panic("implement me") +} + +func (f fakeExecutor) SetVitessMetadata(ctx context.Context, name, value string) error { + // TODO implement me + panic("implement me") +} + +func (f fakeExecutor) ParseDestinationTarget(targetString string) (string, topodatapb.TabletType, key.Destination, error) { + // TODO implement me + panic("implement me") +} + +func (f fakeExecutor) VSchema() *vindexes.VSchema { + // TODO implement me + panic("implement me") +} + +func (f fakeExecutor) PlanPrepareStmt(ctx context.Context, vcursor *VCursorImpl, query string) (*engine.Plan, sqlparser.Statement, error) { + // TODO implement me + panic("implement me") +} + +func (f fakeExecutor) Environment() *vtenv.Environment { + return vtenv.NewTestEnv() +} + +func (f fakeExecutor) ReadTransaction(ctx context.Context, transactionID string) (*querypb.TransactionMetadata, error) { + // TODO implement me + panic("implement me") +} + +func (f fakeExecutor) UnresolvedTransactions(ctx context.Context, targets []*querypb.Target) ([]*querypb.TransactionMetadata, error) { + // TODO implement me + panic("implement me") +} + +func (f fakeExecutor) AddWarningCount(name string, value int64) { + // TODO implement me + panic("implement me") +} + +var _ iExecute = (*fakeExecutor)(nil) + +type fakeObserver struct{} + +func (f fakeObserver) Observe(*sqltypes.Result) { +} + +var _ ResultsObserver = (*fakeObserver)(nil) diff --git a/go/vt/vtgate/legacy_scatter_conn_test.go b/go/vt/vtgate/legacy_scatter_conn_test.go index 4512fc0724e..0d49e7b7bd9 100644 --- a/go/vt/vtgate/legacy_scatter_conn_test.go +++ b/go/vt/vtgate/legacy_scatter_conn_test.go @@ -26,6 +26,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + econtext "vitess.io/vitess/go/vt/vtgate/executorcontext" + "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/sqltypes" @@ -99,7 +101,7 @@ func TestLegacyExecuteFailOnAutocommit(t *testing.T) { }, Autocommit: false, } - _, errs := sc.ExecuteMultiShard(ctx, nil, rss, queries, NewSafeSession(session), true /*autocommit*/, false, nullResultsObserver{}) + _, errs := sc.ExecuteMultiShard(ctx, nil, rss, queries, econtext.NewSafeSession(session), true /*autocommit*/, false, nullResultsObserver{}) err := vterrors.Aggregate(errs) require.Error(t, err) require.Contains(t, err.Error(), "in autocommit mode, transactionID should be zero but was: 123") @@ -123,7 +125,7 @@ func TestScatterConnExecuteMulti(t *testing.T) { } } - qr, errs := sc.ExecuteMultiShard(ctx, nil, rss, queries, NewSafeSession(nil), false /*autocommit*/, false, nullResultsObserver{}) + qr, errs := sc.ExecuteMultiShard(ctx, nil, rss, queries, econtext.NewSafeSession(nil), false /*autocommit*/, false, nullResultsObserver{}) return qr, vterrors.Aggregate(errs) }) } @@ -138,7 +140,7 @@ func TestScatterConnStreamExecuteMulti(t *testing.T) { bvs := make([]map[string]*querypb.BindVariable, len(rss)) qr := new(sqltypes.Result) var mu sync.Mutex - errors := sc.StreamExecuteMulti(ctx, nil, "query", rss, bvs, NewSafeSession(&vtgatepb.Session{InTransaction: true}), true /* autocommit */, func(r *sqltypes.Result) error { + errors := sc.StreamExecuteMulti(ctx, nil, "query", rss, bvs, econtext.NewSafeSession(&vtgatepb.Session{InTransaction: true}), true /* autocommit */, func(r *sqltypes.Result) error { mu.Lock() defer mu.Unlock() qr.AppendResult(r) @@ -280,7 +282,7 @@ func TestMaxMemoryRows(t *testing.T) { []key.Destination{key.DestinationShard("0"), key.DestinationShard("1")}) require.NoError(t, err) - session := NewSafeSession(&vtgatepb.Session{InTransaction: true}) + session := econtext.NewSafeSession(&vtgatepb.Session{InTransaction: true}) queries := []*querypb.BoundQuery{{ Sql: "query1", BindVariables: map[string]*querypb.BindVariable{}, @@ -328,7 +330,7 @@ func TestLegaceHealthCheckFailsOnReservedConnections(t *testing.T) { res := srvtopo.NewResolver(newSandboxForCells(ctx, []string{"aa"}), sc.gateway, "aa") - session := NewSafeSession(&vtgatepb.Session{InTransaction: false, InReservedConn: true}) + session := econtext.NewSafeSession(&vtgatepb.Session{InTransaction: false, InReservedConn: true}) destinations := []key.Destination{key.DestinationShard("0")} rss, _, err := res.ResolveDestinations(ctx, keyspace, topodatapb.TabletType_REPLICA, nil, destinations) require.NoError(t, err) @@ -346,12 +348,12 @@ func TestLegaceHealthCheckFailsOnReservedConnections(t *testing.T) { require.Error(t, vterrors.Aggregate(errs)) } -func executeOnShards(t *testing.T, ctx context.Context, res *srvtopo.Resolver, keyspace string, sc *ScatterConn, session *SafeSession, destinations []key.Destination) { +func executeOnShards(t *testing.T, ctx context.Context, res *srvtopo.Resolver, keyspace string, sc *ScatterConn, session *econtext.SafeSession, destinations []key.Destination) { t.Helper() require.Empty(t, executeOnShardsReturnsErr(t, ctx, res, keyspace, sc, session, destinations)) } -func executeOnShardsReturnsErr(t *testing.T, ctx context.Context, res *srvtopo.Resolver, keyspace string, sc *ScatterConn, session *SafeSession, destinations []key.Destination) error { +func executeOnShardsReturnsErr(t *testing.T, ctx context.Context, res *srvtopo.Resolver, keyspace string, sc *ScatterConn, session *econtext.SafeSession, destinations []key.Destination) error { t.Helper() rss, _, err := res.ResolveDestinations(ctx, keyspace, topodatapb.TabletType_REPLICA, nil, destinations) require.NoError(t, err) @@ -374,7 +376,7 @@ type recordingResultsObserver struct { recorded []*sqltypes.Result } -func (o *recordingResultsObserver) observe(result *sqltypes.Result) { +func (o *recordingResultsObserver) Observe(result *sqltypes.Result) { mu.Lock() o.recorded = append(o.recorded, result) mu.Unlock() @@ -429,7 +431,7 @@ func TestMultiExecs(t *testing.T) { observer := recordingResultsObserver{} - session := NewSafeSession(&vtgatepb.Session{}) + session := econtext.NewSafeSession(&vtgatepb.Session{}) _, err := sc.ExecuteMultiShard(ctx, nil, rss, queries, session, false, false, &observer) require.NoError(t, vterrors.Aggregate(err)) if len(sbc0.Queries) == 0 || len(sbc1.Queries) == 0 { @@ -511,7 +513,7 @@ func TestScatterConnSingleDB(t *testing.T) { want := "multi-db transaction attempted" // TransactionMode_SINGLE in session - session := NewSafeSession(&vtgatepb.Session{InTransaction: true, TransactionMode: vtgatepb.TransactionMode_SINGLE}) + session := econtext.NewSafeSession(&vtgatepb.Session{InTransaction: true, TransactionMode: vtgatepb.TransactionMode_SINGLE}) queries := []*querypb.BoundQuery{{Sql: "query1"}} _, errors := sc.ExecuteMultiShard(ctx, nil, rss0, queries, session, false, false, nullResultsObserver{}) require.Empty(t, errors) @@ -521,7 +523,7 @@ func TestScatterConnSingleDB(t *testing.T) { // TransactionMode_SINGLE in txconn sc.txConn.mode = vtgatepb.TransactionMode_SINGLE - session = NewSafeSession(&vtgatepb.Session{InTransaction: true}) + session = econtext.NewSafeSession(&vtgatepb.Session{InTransaction: true}) _, errors = sc.ExecuteMultiShard(ctx, nil, rss0, queries, session, false, false, nullResultsObserver{}) require.Empty(t, errors) _, errors = sc.ExecuteMultiShard(ctx, nil, rss1, queries, session, false, false, nullResultsObserver{}) @@ -530,7 +532,7 @@ func TestScatterConnSingleDB(t *testing.T) { // TransactionMode_MULTI in txconn. Should not fail. sc.txConn.mode = vtgatepb.TransactionMode_MULTI - session = NewSafeSession(&vtgatepb.Session{InTransaction: true}) + session = econtext.NewSafeSession(&vtgatepb.Session{InTransaction: true}) _, errors = sc.ExecuteMultiShard(ctx, nil, rss0, queries, session, false, false, nullResultsObserver{}) require.Empty(t, errors) _, errors = sc.ExecuteMultiShard(ctx, nil, rss1, queries, session, false, false, nullResultsObserver{}) @@ -601,7 +603,7 @@ func TestReservePrequeries(t *testing.T) { res := srvtopo.NewResolver(newSandboxForCells(ctx, []string{"aa"}), sc.gateway, "aa") - session := NewSafeSession(&vtgatepb.Session{ + session := econtext.NewSafeSession(&vtgatepb.Session{ InTransaction: false, InReservedConn: true, SystemVariables: map[string]string{ diff --git a/go/vt/vtgate/plan_execute.go b/go/vt/vtgate/plan_execute.go index 1c0915470ef..db7923c09f0 100644 --- a/go/vt/vtgate/plan_execute.go +++ b/go/vt/vtgate/plan_execute.go @@ -29,11 +29,12 @@ import ( "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vtgate/engine" + econtext "vitess.io/vitess/go/vt/vtgate/executorcontext" "vitess.io/vitess/go/vt/vtgate/logstats" "vitess.io/vitess/go/vt/vtgate/vtgateservice" ) -type planExec func(ctx context.Context, plan *engine.Plan, vc *vcursorImpl, bindVars map[string]*querypb.BindVariable, startTime time.Time) error +type planExec func(ctx context.Context, plan *engine.Plan, vc *econtext.VCursorImpl, bindVars map[string]*querypb.BindVariable, startTime time.Time) error type txResult func(sqlparser.StatementType, *sqltypes.Result) error var vschemaWaitTimeout = 30 * time.Second @@ -56,10 +57,12 @@ func waitForNewerVSchema(ctx context.Context, e *Executor, lastVSchemaCreated ti } } +const MaxBufferingRetries = 3 + func (e *Executor) newExecute( ctx context.Context, mysqlCtx vtgateservice.MySQLConnection, - safeSession *SafeSession, + safeSession *econtext.SafeSession, sql string, bindVars map[string]*querypb.BindVariable, logStats *logstats.LogStats, @@ -116,7 +119,7 @@ func (e *Executor) newExecute( } } - vcursor, err := newVCursorImpl(safeSession, comments, e, logStats, e.vm, vs, e.resolver.resolver, e.serv, e.warnShardedOnly, e.pv) + vcursor, err := econtext.NewVCursorImpl(safeSession, comments, e, logStats, e.vm, vs, e.resolver.resolver, e.serv, nullResultsObserver{}, e.vConfig) if err != nil { return err } @@ -146,10 +149,8 @@ func (e *Executor) newExecute( } // set the overall query timeout if it is not already set - if vcursor.queryTimeout > 0 && cancel == nil { - ctx, cancel = context.WithTimeout(ctx, vcursor.queryTimeout) - defer cancel() - } + ctx, cancel = vcursor.GetContextWithTimeOut(ctx) + defer cancel() result, err = e.handleTransactions(ctx, mysqlCtx, safeSession, plan, logStats, vcursor, stmt) if err != nil { @@ -225,10 +226,10 @@ func (e *Executor) newExecute( func (e *Executor) handleTransactions( ctx context.Context, mysqlCtx vtgateservice.MySQLConnection, - safeSession *SafeSession, + safeSession *econtext.SafeSession, plan *engine.Plan, logStats *logstats.LogStats, - vcursor *vcursorImpl, + vcursor *econtext.VCursorImpl, stmt sqlparser.Statement, ) (*sqltypes.Result, error) { // We need to explicitly handle errors, and begin/commit/rollback, since these control transactions. Everything else @@ -247,19 +248,19 @@ func (e *Executor) handleTransactions( qr, err := e.handleSavepoint(ctx, safeSession, plan.Original, "Savepoint", logStats, func(_ string) (*sqltypes.Result, error) { // Safely to ignore as there is no transaction. return &sqltypes.Result{}, nil - }, vcursor.ignoreMaxMemoryRows) + }, vcursor.IgnoreMaxMemoryRows()) return qr, err case sqlparser.StmtSRollback: qr, err := e.handleSavepoint(ctx, safeSession, plan.Original, "Rollback Savepoint", logStats, func(query string) (*sqltypes.Result, error) { // Error as there is no transaction, so there is no savepoint that exists. return nil, vterrors.NewErrorf(vtrpcpb.Code_NOT_FOUND, vterrors.SPDoesNotExist, "SAVEPOINT does not exist: %s", query) - }, vcursor.ignoreMaxMemoryRows) + }, vcursor.IgnoreMaxMemoryRows()) return qr, err case sqlparser.StmtRelease: qr, err := e.handleSavepoint(ctx, safeSession, plan.Original, "Release Savepoint", logStats, func(query string) (*sqltypes.Result, error) { // Error as there is no transaction, so there is no savepoint that exists. return nil, vterrors.NewErrorf(vtrpcpb.Code_NOT_FOUND, vterrors.SPDoesNotExist, "SAVEPOINT does not exist: %s", query) - }, vcursor.ignoreMaxMemoryRows) + }, vcursor.IgnoreMaxMemoryRows()) return qr, err case sqlparser.StmtKill: return e.handleKill(ctx, mysqlCtx, stmt, logStats) @@ -267,7 +268,7 @@ func (e *Executor) handleTransactions( return nil, nil } -func (e *Executor) startTxIfNecessary(ctx context.Context, safeSession *SafeSession) error { +func (e *Executor) startTxIfNecessary(ctx context.Context, safeSession *econtext.SafeSession) error { if !safeSession.Autocommit && !safeSession.InTransaction() { if err := e.txConn.Begin(ctx, safeSession, nil); err != nil { return err @@ -276,7 +277,7 @@ func (e *Executor) startTxIfNecessary(ctx context.Context, safeSession *SafeSess return nil } -func (e *Executor) insideTransaction(ctx context.Context, safeSession *SafeSession, logStats *logstats.LogStats, execPlan func() error) error { +func (e *Executor) insideTransaction(ctx context.Context, safeSession *econtext.SafeSession, logStats *logstats.LogStats, execPlan func() error) error { mustCommit := false if safeSession.Autocommit && !safeSession.InTransaction() { mustCommit = true @@ -320,9 +321,9 @@ func (e *Executor) insideTransaction(ctx context.Context, safeSession *SafeSessi func (e *Executor) executePlan( ctx context.Context, - safeSession *SafeSession, + safeSession *econtext.SafeSession, plan *engine.Plan, - vcursor *vcursorImpl, + vcursor *econtext.VCursorImpl, bindVars map[string]*querypb.BindVariable, logStats *logstats.LogStats, execStart time.Time, @@ -342,7 +343,7 @@ func (e *Executor) executePlan( } // rollbackExecIfNeeded rollbacks the partial execution if earlier it was detected that it needs partial query execution to be rolled back. -func (e *Executor) rollbackExecIfNeeded(ctx context.Context, safeSession *SafeSession, bindVars map[string]*querypb.BindVariable, logStats *logstats.LogStats, err error) error { +func (e *Executor) rollbackExecIfNeeded(ctx context.Context, safeSession *econtext.SafeSession, bindVars map[string]*querypb.BindVariable, logStats *logstats.LogStats, err error) error { if safeSession.InTransaction() && safeSession.IsRollbackSet() { rErr := e.rollbackPartialExec(ctx, safeSession, bindVars, logStats) return vterrors.Wrap(err, rErr.Error()) @@ -353,7 +354,7 @@ func (e *Executor) rollbackExecIfNeeded(ctx context.Context, safeSession *SafeSe // rollbackPartialExec rollbacks to the savepoint or rollbacks transaction based on the value set on SafeSession.rollbackOnPartialExec. // Once, it is used the variable is reset. // If it fails to rollback to the previous savepoint then, the transaction is forced to be rolled back. -func (e *Executor) rollbackPartialExec(ctx context.Context, safeSession *SafeSession, bindVars map[string]*querypb.BindVariable, logStats *logstats.LogStats) error { +func (e *Executor) rollbackPartialExec(ctx context.Context, safeSession *econtext.SafeSession, bindVars map[string]*querypb.BindVariable, logStats *logstats.LogStats) error { var err error var errMsg strings.Builder @@ -367,8 +368,8 @@ func (e *Executor) rollbackPartialExec(ctx context.Context, safeSession *SafeSes } // needs to rollback only once. - rQuery := safeSession.rollbackOnPartialExec - if rQuery != txRollback { + rQuery := safeSession.GetRollbackOnPartialExec() + if rQuery != econtext.TxRollback { safeSession.SavepointRollback() _, _, err = e.execute(ctx, nil, safeSession, rQuery, bindVars, logStats) // If no error, the revert is successful with the savepoint. Notify the reason as error to the client. @@ -388,9 +389,9 @@ func (e *Executor) rollbackPartialExec(ctx context.Context, safeSession *SafeSes return vterrors.New(vtrpcpb.Code_ABORTED, errMsg.String()) } -func (e *Executor) setLogStats(logStats *logstats.LogStats, plan *engine.Plan, vcursor *vcursorImpl, execStart time.Time, err error, qr *sqltypes.Result) { +func (e *Executor) setLogStats(logStats *logstats.LogStats, plan *engine.Plan, vcursor *econtext.VCursorImpl, execStart time.Time, err error, qr *sqltypes.Result) { logStats.StmtType = plan.Type.String() - logStats.ActiveKeyspace = vcursor.keyspace + logStats.ActiveKeyspace = vcursor.GetKeyspace() logStats.TablesUsed = plan.TablesUsed logStats.TabletType = vcursor.TabletType().String() errCount := e.logExecutionEnd(logStats, execStart, plan, err, qr) diff --git a/go/vt/vtgate/planbuilder/builder.go b/go/vt/vtgate/planbuilder/builder.go index 27b994b1730..ca4ccb7ac5a 100644 --- a/go/vt/vtgate/planbuilder/builder.go +++ b/go/vt/vtgate/planbuilder/builder.go @@ -28,6 +28,7 @@ import ( topodatapb "vitess.io/vitess/go/vt/proto/topodata" "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vterrors" + "vitess.io/vitess/go/vt/vtgate/dynamicconfig" "vitess.io/vitess/go/vt/vtgate/engine" "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" "vitess.io/vitess/go/vt/vtgate/vindexes" @@ -63,6 +64,16 @@ func singleTable(ks, tbl string) string { return fmt.Sprintf("%s.%s", ks, tbl) } +type staticConfig struct{} + +func (staticConfig) OnlineEnabled() bool { + return true +} + +func (staticConfig) DirectEnabled() bool { + return true +} + // TestBuilder builds a plan for a query based on the specified vschema. // This method is only used from tests func TestBuilder(query string, vschema plancontext.VSchema, keyspace string) (*engine.Plan, error) { @@ -92,12 +103,12 @@ func TestBuilder(query string, vschema plancontext.VSchema, keyspace string) (*e } reservedVars := sqlparser.NewReservedVars("vtg", reserved) - return BuildFromStmt(context.Background(), query, result.AST, reservedVars, vschema, result.BindVarNeeds, true, true) + return BuildFromStmt(context.Background(), query, result.AST, reservedVars, vschema, result.BindVarNeeds, staticConfig{}) } // BuildFromStmt builds a plan based on the AST provided. -func BuildFromStmt(ctx context.Context, query string, stmt sqlparser.Statement, reservedVars *sqlparser.ReservedVars, vschema plancontext.VSchema, bindVarNeeds *sqlparser.BindVarNeeds, enableOnlineDDL, enableDirectDDL bool) (*engine.Plan, error) { - planResult, err := createInstructionFor(ctx, query, stmt, reservedVars, vschema, enableOnlineDDL, enableDirectDDL) +func BuildFromStmt(ctx context.Context, query string, stmt sqlparser.Statement, reservedVars *sqlparser.ReservedVars, vschema plancontext.VSchema, bindVarNeeds *sqlparser.BindVarNeeds, cfg dynamicconfig.DDL) (*engine.Plan, error) { + planResult, err := createInstructionFor(ctx, query, stmt, reservedVars, vschema, cfg) if err != nil { return nil, err } @@ -154,7 +165,7 @@ func buildRoutePlan(stmt sqlparser.Statement, reservedVars *sqlparser.ReservedVa return f(stmt, reservedVars, vschema) } -func createInstructionFor(ctx context.Context, query string, stmt sqlparser.Statement, reservedVars *sqlparser.ReservedVars, vschema plancontext.VSchema, enableOnlineDDL, enableDirectDDL bool) (*planResult, error) { +func createInstructionFor(ctx context.Context, query string, stmt sqlparser.Statement, reservedVars *sqlparser.ReservedVars, vschema plancontext.VSchema, cfg dynamicconfig.DDL) (*planResult, error) { switch stmt := stmt.(type) { case *sqlparser.Select, *sqlparser.Insert, *sqlparser.Update, *sqlparser.Delete: configuredPlanner, err := getConfiguredPlanner(vschema, stmt, query) @@ -169,13 +180,13 @@ func createInstructionFor(ctx context.Context, query string, stmt sqlparser.Stat } return buildRoutePlan(stmt, reservedVars, vschema, configuredPlanner) case sqlparser.DDLStatement: - return buildGeneralDDLPlan(ctx, query, stmt, reservedVars, vschema, enableOnlineDDL, enableDirectDDL) + return buildGeneralDDLPlan(ctx, query, stmt, reservedVars, vschema, cfg) case *sqlparser.AlterMigration: - return buildAlterMigrationPlan(query, stmt, vschema, enableOnlineDDL) + return buildAlterMigrationPlan(query, stmt, vschema, cfg) case *sqlparser.RevertMigration: - return buildRevertMigrationPlan(query, stmt, vschema, enableOnlineDDL) + return buildRevertMigrationPlan(query, stmt, vschema, cfg) case *sqlparser.ShowMigrationLogs: - return buildShowMigrationLogsPlan(query, vschema, enableOnlineDDL) + return buildShowMigrationLogsPlan(query, vschema, cfg) case *sqlparser.ShowThrottledApps: return buildShowThrottledAppsPlan(query, vschema) case *sqlparser.ShowThrottlerStatus: @@ -189,7 +200,7 @@ func createInstructionFor(ctx context.Context, query string, stmt sqlparser.Stat case *sqlparser.ExplainStmt: return buildRoutePlan(stmt, reservedVars, vschema, buildExplainStmtPlan) case *sqlparser.VExplainStmt: - return buildVExplainPlan(ctx, stmt, reservedVars, vschema, enableOnlineDDL, enableDirectDDL) + return buildVExplainPlan(ctx, stmt, reservedVars, vschema, cfg) case *sqlparser.OtherAdmin: return buildOtherReadAndAdmin(query, vschema) case *sqlparser.Analyze: @@ -275,7 +286,7 @@ func buildDBDDLPlan(stmt sqlparser.Statement, _ *sqlparser.ReservedVars, vschema dbDDLstmt := stmt.(sqlparser.DBDDLStatement) ksName := dbDDLstmt.GetDatabaseName() if ksName == "" { - ks, err := vschema.DefaultKeyspace() + ks, err := vschema.SelectedKeyspace() if err != nil { return nil, err } @@ -310,7 +321,7 @@ func buildDBDDLPlan(stmt sqlparser.Statement, _ *sqlparser.ReservedVars, vschema } func buildLoadPlan(query string, vschema plancontext.VSchema) (*planResult, error) { - keyspace, err := vschema.DefaultKeyspace() + keyspace, err := vschema.SelectedKeyspace() if err != nil { return nil, err } @@ -355,7 +366,7 @@ func buildFlushOptions(stmt *sqlparser.Flush, vschema plancontext.VSchema) (*pla return nil, vterrors.VT09012("FLUSH", vschema.TabletType().String()) } - keyspace, err := vschema.DefaultKeyspace() + keyspace, err := vschema.SelectedKeyspace() if err != nil { return nil, err } diff --git a/go/vt/vtgate/planbuilder/bypass.go b/go/vt/vtgate/planbuilder/bypass.go index 62cae9655b1..d3384d509c1 100644 --- a/go/vt/vtgate/planbuilder/bypass.go +++ b/go/vt/vtgate/planbuilder/bypass.go @@ -26,7 +26,7 @@ import ( ) func buildPlanForBypass(stmt sqlparser.Statement, _ *sqlparser.ReservedVars, vschema plancontext.VSchema) (*planResult, error) { - keyspace, err := vschema.DefaultKeyspace() + keyspace, err := vschema.SelectedKeyspace() if err != nil { return nil, err } diff --git a/go/vt/vtgate/planbuilder/collations_test.go b/go/vt/vtgate/planbuilder/collations_test.go index b393e186679..0595039e673 100644 --- a/go/vt/vtgate/planbuilder/collations_test.go +++ b/go/vt/vtgate/planbuilder/collations_test.go @@ -41,15 +41,13 @@ type collationTestCase struct { } func (tc *collationTestCase) run(t *testing.T) { - vschemaWrapper := &vschemawrapper.VSchemaWrapper{ - V: loadSchema(t, "vschemas/schema.json", false), - SysVarEnabled: true, - Version: Gen4, - Env: vtenv.NewTestEnv(), - } + env := vtenv.NewTestEnv() + vschema := loadSchema(t, "vschemas/schema.json", true) + vw, err := vschemawrapper.NewVschemaWrapper(env, vschema, TestBuilder) + require.NoError(t, err) - tc.addCollationsToSchema(vschemaWrapper) - plan, err := TestBuilder(tc.query, vschemaWrapper, vschemaWrapper.CurrentDb()) + tc.addCollationsToSchema(vw) + plan, err := TestBuilder(tc.query, vw, vw.CurrentDb()) require.NoError(t, err) tc.check(t, tc.collations, plan.Instructions) } diff --git a/go/vt/vtgate/planbuilder/ddl.go b/go/vt/vtgate/planbuilder/ddl.go index f4b8ab6976f..a0045cec060 100644 --- a/go/vt/vtgate/planbuilder/ddl.go +++ b/go/vt/vtgate/planbuilder/ddl.go @@ -9,6 +9,7 @@ import ( vschemapb "vitess.io/vitess/go/vt/proto/vschema" "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vterrors" + "vitess.io/vitess/go/vt/vtgate/dynamicconfig" "vitess.io/vitess/go/vt/vtgate/engine" "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" "vitess.io/vitess/go/vt/vtgate/vindexes" @@ -43,11 +44,11 @@ func (fk *fkContraint) FkWalk(node sqlparser.SQLNode) (kontinue bool, err error) // a session context. It's only when we Execute() the primitive that we have that context. // This is why we return a compound primitive (DDL) which contains fully populated primitives (Send & OnlineDDL), // and which chooses which of the two to invoke at runtime. -func buildGeneralDDLPlan(ctx context.Context, sql string, ddlStatement sqlparser.DDLStatement, reservedVars *sqlparser.ReservedVars, vschema plancontext.VSchema, enableOnlineDDL, enableDirectDDL bool) (*planResult, error) { +func buildGeneralDDLPlan(ctx context.Context, sql string, ddlStatement sqlparser.DDLStatement, reservedVars *sqlparser.ReservedVars, vschema plancontext.VSchema, cfg dynamicconfig.DDL) (*planResult, error) { if vschema.Destination() != nil { return buildByPassPlan(sql, vschema, true) } - normalDDLPlan, onlineDDLPlan, err := buildDDLPlans(ctx, sql, ddlStatement, reservedVars, vschema, enableOnlineDDL, enableDirectDDL) + normalDDLPlan, onlineDDLPlan, err := buildDDLPlans(ctx, sql, ddlStatement, reservedVars, vschema, cfg) if err != nil { return nil, err } @@ -61,15 +62,12 @@ func buildGeneralDDLPlan(ctx context.Context, sql string, ddlStatement sqlparser } eddl := &engine.DDL{ - Keyspace: normalDDLPlan.Keyspace, - SQL: normalDDLPlan.Query, - DDL: ddlStatement, - NormalDDL: normalDDLPlan, - OnlineDDL: onlineDDLPlan, - - DirectDDLEnabled: enableDirectDDL, - OnlineDDLEnabled: enableOnlineDDL, - + Keyspace: normalDDLPlan.Keyspace, + SQL: normalDDLPlan.Query, + DDL: ddlStatement, + NormalDDL: normalDDLPlan, + OnlineDDL: onlineDDLPlan, + Config: cfg, CreateTempTable: ddlStatement.IsTemporary(), } tc := &tableCollector{} @@ -81,7 +79,7 @@ func buildGeneralDDLPlan(ctx context.Context, sql string, ddlStatement sqlparser } func buildByPassPlan(sql string, vschema plancontext.VSchema, isDDL bool) (*planResult, error) { - keyspace, err := vschema.DefaultKeyspace() + keyspace, err := vschema.SelectedKeyspace() if err != nil { return nil, err } @@ -94,7 +92,7 @@ func buildByPassPlan(sql string, vschema plancontext.VSchema, isDDL bool) (*plan return newPlanResult(send), nil } -func buildDDLPlans(ctx context.Context, sql string, ddlStatement sqlparser.DDLStatement, reservedVars *sqlparser.ReservedVars, vschema plancontext.VSchema, enableOnlineDDL, enableDirectDDL bool) (*engine.Send, *engine.OnlineDDL, error) { +func buildDDLPlans(ctx context.Context, sql string, ddlStatement sqlparser.DDLStatement, reservedVars *sqlparser.ReservedVars, vschema plancontext.VSchema, cfg dynamicconfig.DDL) (*engine.Send, *engine.OnlineDDL, error) { var destination key.Destination var keyspace *vindexes.Keyspace var err error @@ -113,9 +111,9 @@ func buildDDLPlans(ctx context.Context, sql string, ddlStatement sqlparser.DDLSt } err = checkFKError(vschema, ddlStatement, keyspace) case *sqlparser.CreateView: - destination, keyspace, err = buildCreateViewCommon(ctx, vschema, reservedVars, enableOnlineDDL, enableDirectDDL, ddl.Select, ddl) + destination, keyspace, err = buildCreateViewCommon(ctx, vschema, reservedVars, cfg, ddl.Select, ddl) case *sqlparser.AlterView: - destination, keyspace, err = buildCreateViewCommon(ctx, vschema, reservedVars, enableOnlineDDL, enableDirectDDL, ddl.Select, ddl) + destination, keyspace, err = buildCreateViewCommon(ctx, vschema, reservedVars, cfg, ddl.Select, ddl) case *sqlparser.DropView: destination, keyspace, err = buildDropView(vschema, ddlStatement) case *sqlparser.DropTable: @@ -197,7 +195,7 @@ func buildCreateViewCommon( ctx context.Context, vschema plancontext.VSchema, reservedVars *sqlparser.ReservedVars, - enableOnlineDDL, enableDirectDDL bool, + cfg dynamicconfig.DDL, ddlSelect sqlparser.SelectStatement, ddl sqlparser.DDLStatement, ) (key.Destination, *vindexes.Keyspace, error) { @@ -214,7 +212,7 @@ func buildCreateViewCommon( expressions = append(expressions, sqlparser.Clone(p.SelectExprs)) return nil }) - selectPlan, err := createInstructionFor(ctx, sqlparser.String(ddlSelect), ddlSelect, reservedVars, vschema, enableOnlineDDL, enableDirectDDL) + selectPlan, err := createInstructionFor(ctx, sqlparser.String(ddlSelect), ddlSelect, reservedVars, vschema, cfg) if err != nil { return nil, nil, err } diff --git a/go/vt/vtgate/planbuilder/migration.go b/go/vt/vtgate/planbuilder/migration.go index 6fb73a9039d..e64b990aa6b 100644 --- a/go/vt/vtgate/planbuilder/migration.go +++ b/go/vt/vtgate/planbuilder/migration.go @@ -27,6 +27,7 @@ import ( "vitess.io/vitess/go/vt/schema" "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vterrors" + "vitess.io/vitess/go/vt/vtgate/dynamicconfig" "vitess.io/vitess/go/vt/vtgate/engine" "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" "vitess.io/vitess/go/vt/vtgate/vindexes" @@ -80,8 +81,8 @@ func buildAlterMigrationThrottleAppPlan(query string, alterMigration *sqlparser. }), nil } -func buildAlterMigrationPlan(query string, alterMigration *sqlparser.AlterMigration, vschema plancontext.VSchema, enableOnlineDDL bool) (*planResult, error) { - if !enableOnlineDDL { +func buildAlterMigrationPlan(query string, alterMigration *sqlparser.AlterMigration, vschema plancontext.VSchema, cfg dynamicconfig.DDL) (*planResult, error) { + if !cfg.OnlineEnabled() { return nil, schema.ErrOnlineDDLDisabled } @@ -118,8 +119,8 @@ func buildAlterMigrationPlan(query string, alterMigration *sqlparser.AlterMigrat return newPlanResult(send), nil } -func buildRevertMigrationPlan(query string, stmt *sqlparser.RevertMigration, vschema plancontext.VSchema, enableOnlineDDL bool) (*planResult, error) { - if !enableOnlineDDL { +func buildRevertMigrationPlan(query string, stmt *sqlparser.RevertMigration, vschema plancontext.VSchema, cfg dynamicconfig.DDL) (*planResult, error) { + if !cfg.OnlineEnabled() { return nil, schema.ErrOnlineDDLDisabled } dest, ks, tabletType, err := vschema.TargetDestination("") @@ -147,8 +148,8 @@ func buildRevertMigrationPlan(query string, stmt *sqlparser.RevertMigration, vsc return newPlanResult(emig), nil } -func buildShowMigrationLogsPlan(query string, vschema plancontext.VSchema, enableOnlineDDL bool) (*planResult, error) { - if !enableOnlineDDL { +func buildShowMigrationLogsPlan(query string, vschema plancontext.VSchema, cfg dynamicconfig.DDL) (*planResult, error) { + if !cfg.OnlineEnabled() { return nil, schema.ErrOnlineDDLDisabled } dest, ks, tabletType, err := vschema.TargetDestination("") diff --git a/go/vt/vtgate/planbuilder/operator_transformers.go b/go/vt/vtgate/planbuilder/operator_transformers.go index a22719b4489..df14745e6b2 100644 --- a/go/vt/vtgate/planbuilder/operator_transformers.go +++ b/go/vt/vtgate/planbuilder/operator_transformers.go @@ -545,7 +545,7 @@ func routeToEngineRoute(ctx *plancontext.PlanningContext, op *operators.Route, h } func newRoutingParams(ctx *plancontext.PlanningContext, opCode engine.Opcode) *engine.RoutingParameters { - ks, _ := ctx.VSchema.DefaultKeyspace() + ks, _ := ctx.VSchema.SelectedKeyspace() if ks == nil { // if we don't have a selected keyspace, any keyspace will do // this is used by operators that do not set the keyspace diff --git a/go/vt/vtgate/planbuilder/plan_test.go b/go/vt/vtgate/planbuilder/plan_test.go index acba2caf937..ccbc9821170 100644 --- a/go/vt/vtgate/planbuilder/plan_test.go +++ b/go/vt/vtgate/planbuilder/plan_test.go @@ -74,17 +74,16 @@ func TestPlanTestSuite(t *testing.T) { func (s *planTestSuite) TestPlan() { defer utils.EnsureNoLeaks(s.T()) - vschemaWrapper := &vschemawrapper.VSchemaWrapper{ - V: loadSchema(s.T(), "vschemas/schema.json", true), - TabletType_: topodatapb.TabletType_PRIMARY, - SysVarEnabled: true, - TestBuilder: TestBuilder, - Env: vtenv.NewTestEnv(), - } - s.addPKs(vschemaWrapper.V, "user", []string{"user", "music"}) - s.addPKsProvided(vschemaWrapper.V, "user", []string{"user_extra"}, []string{"id", "user_id"}) - s.addPKsProvided(vschemaWrapper.V, "ordering", []string{"order"}, []string{"oid", "region_id"}) - s.addPKsProvided(vschemaWrapper.V, "ordering", []string{"order_event"}, []string{"oid", "ename"}) + + env := vtenv.NewTestEnv() + vschema := loadSchema(s.T(), "vschemas/schema.json", true) + vw, err := vschemawrapper.NewVschemaWrapper(env, vschema, TestBuilder) + require.NoError(s.T(), err) + + s.addPKs(vschema, "user", []string{"user", "music"}) + s.addPKsProvided(vschema, "user", []string{"user_extra"}, []string{"id", "user_id"}) + s.addPKsProvided(vschema, "ordering", []string{"order"}, []string{"oid", "region_id"}) + s.addPKsProvided(vschema, "ordering", []string{"order_event"}, []string{"oid", "ename"}) // You will notice that some tests expect user.Id instead of user.id. // This is because we now pre-create vindex columns in the symbol @@ -92,77 +91,73 @@ func (s *planTestSuite) TestPlan() { // the column is named as Id. This is to make sure that // column names are case-preserved, but treated as // case-insensitive even if they come from the vschema. - s.testFile("aggr_cases.json", vschemaWrapper, false) - s.testFile("dml_cases.json", vschemaWrapper, false) - s.testFile("from_cases.json", vschemaWrapper, false) - s.testFile("filter_cases.json", vschemaWrapper, false) - s.testFile("postprocess_cases.json", vschemaWrapper, false) - s.testFile("select_cases.json", vschemaWrapper, false) - s.testFile("symtab_cases.json", vschemaWrapper, false) - s.testFile("unsupported_cases.json", vschemaWrapper, false) - s.testFile("unknown_schema_cases.json", vschemaWrapper, false) - s.testFile("vindex_func_cases.json", vschemaWrapper, false) - s.testFile("wireup_cases.json", vschemaWrapper, false) - s.testFile("memory_sort_cases.json", vschemaWrapper, false) - s.testFile("use_cases.json", vschemaWrapper, false) - s.testFile("set_cases.json", vschemaWrapper, false) - s.testFile("union_cases.json", vschemaWrapper, false) - s.testFile("large_union_cases.json", vschemaWrapper, false) - s.testFile("transaction_cases.json", vschemaWrapper, false) - s.testFile("lock_cases.json", vschemaWrapper, false) - s.testFile("large_cases.json", vschemaWrapper, false) - s.testFile("ddl_cases_no_default_keyspace.json", vschemaWrapper, false) - s.testFile("flush_cases_no_default_keyspace.json", vschemaWrapper, false) - s.testFile("show_cases_no_default_keyspace.json", vschemaWrapper, false) - s.testFile("stream_cases.json", vschemaWrapper, false) - s.testFile("info_schema80_cases.json", vschemaWrapper, false) - s.testFile("reference_cases.json", vschemaWrapper, false) - s.testFile("vexplain_cases.json", vschemaWrapper, false) - s.testFile("misc_cases.json", vschemaWrapper, false) - s.testFile("cte_cases.json", vschemaWrapper, false) + s.testFile("aggr_cases.json", vw, false) + s.testFile("dml_cases.json", vw, false) + s.testFile("from_cases.json", vw, false) + s.testFile("filter_cases.json", vw, false) + s.testFile("postprocess_cases.json", vw, false) + s.testFile("select_cases.json", vw, false) + s.testFile("symtab_cases.json", vw, false) + s.testFile("unsupported_cases.json", vw, false) + s.testFile("unknown_schema_cases.json", vw, false) + s.testFile("vindex_func_cases.json", vw, false) + s.testFile("wireup_cases.json", vw, false) + s.testFile("memory_sort_cases.json", vw, false) + s.testFile("use_cases.json", vw, false) + s.testFile("set_cases.json", vw, false) + s.testFile("union_cases.json", vw, false) + s.testFile("large_union_cases.json", vw, false) + s.testFile("transaction_cases.json", vw, false) + s.testFile("lock_cases.json", vw, false) + s.testFile("large_cases.json", vw, false) + s.testFile("ddl_cases_no_default_keyspace.json", vw, false) + s.testFile("flush_cases_no_default_keyspace.json", vw, false) + s.testFile("show_cases_no_default_keyspace.json", vw, false) + s.testFile("stream_cases.json", vw, false) + s.testFile("info_schema80_cases.json", vw, false) + s.testFile("reference_cases.json", vw, false) + s.testFile("vexplain_cases.json", vw, false) + s.testFile("misc_cases.json", vw, false) + s.testFile("cte_cases.json", vw, false) } // TestForeignKeyPlanning tests the planning of foreign keys in a managed mode by Vitess. func (s *planTestSuite) TestForeignKeyPlanning() { + env := vtenv.NewTestEnv() vschema := loadSchema(s.T(), "vschemas/schema.json", true) - s.setFks(vschema) - vschemaWrapper := &vschemawrapper.VSchemaWrapper{ - V: vschema, - TestBuilder: TestBuilder, - Env: vtenv.NewTestEnv(), - } + vw, err := vschemawrapper.NewVschemaWrapper(env, vschema, TestBuilder) + require.NoError(s.T(), err) - s.testFile("foreignkey_cases.json", vschemaWrapper, false) + s.setFks(vschema) + s.testFile("foreignkey_cases.json", vw, false) } // TestForeignKeyChecksOn tests the planning when the session variable for foreign_key_checks is set to ON. func (s *planTestSuite) TestForeignKeyChecksOn() { + env := vtenv.NewTestEnv() vschema := loadSchema(s.T(), "vschemas/schema.json", true) - s.setFks(vschema) + vw, err := vschemawrapper.NewVschemaWrapper(env, vschema, TestBuilder) + require.NoError(s.T(), err) + fkChecksState := true - vschemaWrapper := &vschemawrapper.VSchemaWrapper{ - V: vschema, - TestBuilder: TestBuilder, - ForeignKeyChecksState: &fkChecksState, - Env: vtenv.NewTestEnv(), - } + vw.ForeignKeyChecksState = &fkChecksState - s.testFile("foreignkey_checks_on_cases.json", vschemaWrapper, false) + s.setFks(vschema) + s.testFile("foreignkey_checks_on_cases.json", vw, false) } // TestForeignKeyChecksOff tests the planning when the session variable for foreign_key_checks is set to OFF. func (s *planTestSuite) TestForeignKeyChecksOff() { + env := vtenv.NewTestEnv() vschema := loadSchema(s.T(), "vschemas/schema.json", true) - s.setFks(vschema) + vw, err := vschemawrapper.NewVschemaWrapper(env, vschema, TestBuilder) + require.NoError(s.T(), err) + fkChecksState := false - vschemaWrapper := &vschemawrapper.VSchemaWrapper{ - V: vschema, - TestBuilder: TestBuilder, - ForeignKeyChecksState: &fkChecksState, - Env: vtenv.NewTestEnv(), - } + vw.ForeignKeyChecksState = &fkChecksState - s.testFile("foreignkey_checks_off_cases.json", vschemaWrapper, false) + s.setFks(vschema) + s.testFile("foreignkey_checks_off_cases.json", vw, false) } func (s *planTestSuite) setFks(vschema *vindexes.VSchema) { @@ -266,120 +261,127 @@ func (s *planTestSuite) TestSystemTables57() { MySQLServerVersion: "5.7.9", }) require.NoError(s.T(), err) - vschemaWrapper := &vschemawrapper.VSchemaWrapper{ - V: loadSchema(s.T(), "vschemas/schema.json", true), - Env: env, - } - s.testFile("info_schema57_cases.json", vschemaWrapper, false) + vschema := loadSchema(s.T(), "vschemas/schema.json", true) + vw, err := vschemawrapper.NewVschemaWrapper(env, vschema, TestBuilder) + require.NoError(s.T(), err) + + s.testFile("info_schema57_cases.json", vw, false) } func (s *planTestSuite) TestSysVarSetDisabled() { - vschemaWrapper := &vschemawrapper.VSchemaWrapper{ - V: loadSchema(s.T(), "vschemas/schema.json", true), - SysVarEnabled: false, - Env: vtenv.NewTestEnv(), - } + env := vtenv.NewTestEnv() + vschema := loadSchema(s.T(), "vschemas/schema.json", true) + vw, err := vschemawrapper.NewVschemaWrapper(env, vschema, TestBuilder) + require.NoError(s.T(), err) - s.testFile("set_sysvar_disabled_cases.json", vschemaWrapper, false) + vw.SysVarEnabled = false + + s.testFile("set_sysvar_disabled_cases.json", vw, false) } func (s *planTestSuite) TestViews() { - vschemaWrapper := &vschemawrapper.VSchemaWrapper{ - V: loadSchema(s.T(), "vschemas/schema.json", true), - EnableViews: true, - Env: vtenv.NewTestEnv(), - } + env := vtenv.NewTestEnv() + vschema := loadSchema(s.T(), "vschemas/schema.json", true) + vw, err := vschemawrapper.NewVschemaWrapper(env, vschema, TestBuilder) + require.NoError(s.T(), err) - s.testFile("view_cases.json", vschemaWrapper, false) + vw.EnableViews = true + + s.testFile("view_cases.json", vw, false) } func (s *planTestSuite) TestOne() { reset := operators.EnableDebugPrinting() defer reset() - lv := loadSchema(s.T(), "vschemas/schema.json", true) - s.setFks(lv) - s.addPKs(lv, "user", []string{"user", "music"}) - s.addPKs(lv, "main", []string{"unsharded"}) - s.addPKsProvided(lv, "user", []string{"user_extra"}, []string{"id", "user_id"}) - s.addPKsProvided(lv, "ordering", []string{"order"}, []string{"oid", "region_id"}) - s.addPKsProvided(lv, "ordering", []string{"order_event"}, []string{"oid", "ename"}) - vschema := &vschemawrapper.VSchemaWrapper{ - V: lv, - TestBuilder: TestBuilder, - Env: vtenv.NewTestEnv(), - } + env := vtenv.NewTestEnv() + vschema := loadSchema(s.T(), "vschemas/schema.json", true) + vw, err := vschemawrapper.NewVschemaWrapper(env, vschema, TestBuilder) + require.NoError(s.T(), err) + + s.setFks(vschema) + s.addPKs(vschema, "user", []string{"user", "music"}) + s.addPKs(vschema, "main", []string{"unsharded"}) + s.addPKsProvided(vschema, "user", []string{"user_extra"}, []string{"id", "user_id"}) + s.addPKsProvided(vschema, "ordering", []string{"order"}, []string{"oid", "region_id"}) + s.addPKsProvided(vschema, "ordering", []string{"order_event"}, []string{"oid", "ename"}) - s.testFile("onecase.json", vschema, false) + s.testFile("onecase.json", vw, false) } func (s *planTestSuite) TestOneTPCC() { reset := operators.EnableDebugPrinting() defer reset() - vschema := &vschemawrapper.VSchemaWrapper{ - V: loadSchema(s.T(), "vschemas/tpcc_schema.json", true), - Env: vtenv.NewTestEnv(), - } + env := vtenv.NewTestEnv() + vschema := loadSchema(s.T(), "vschemas/tpcc_schema.json", true) + vw, err := vschemawrapper.NewVschemaWrapper(env, vschema, TestBuilder) + require.NoError(s.T(), err) - s.testFile("onecase.json", vschema, false) + s.testFile("onecase.json", vw, false) } func (s *planTestSuite) TestOneWithMainAsDefault() { reset := operators.EnableDebugPrinting() defer reset() - vschema := &vschemawrapper.VSchemaWrapper{ - V: loadSchema(s.T(), "vschemas/schema.json", true), - Keyspace: &vindexes.Keyspace{ - Name: "main", - Sharded: false, - }, - Env: vtenv.NewTestEnv(), - } - s.testFile("onecase.json", vschema, false) + env := vtenv.NewTestEnv() + vschema := loadSchema(s.T(), "vschemas/schema.json", true) + vw, err := vschemawrapper.NewVschemaWrapper(env, vschema, TestBuilder) + require.NoError(s.T(), err) + + vw.Vcursor.SetTarget("main") + vw.Keyspace = &vindexes.Keyspace{Name: "main"} + + s.testFile("onecase.json", vw, false) } func (s *planTestSuite) TestOneWithSecondUserAsDefault() { reset := operators.EnableDebugPrinting() defer reset() - vschema := &vschemawrapper.VSchemaWrapper{ - V: loadSchema(s.T(), "vschemas/schema.json", true), - Keyspace: &vindexes.Keyspace{ - Name: "second_user", - Sharded: true, - }, - Env: vtenv.NewTestEnv(), + + env := vtenv.NewTestEnv() + vschema := loadSchema(s.T(), "vschemas/schema.json", true) + vw, err := vschemawrapper.NewVschemaWrapper(env, vschema, TestBuilder) + require.NoError(s.T(), err) + + vw.Vcursor.SetTarget("second_user") + vw.Keyspace = &vindexes.Keyspace{ + Name: "second_user", + Sharded: true, } - s.testFile("onecase.json", vschema, false) + s.testFile("onecase.json", vw, false) } func (s *planTestSuite) TestOneWithUserAsDefault() { reset := operators.EnableDebugPrinting() defer reset() - vschema := &vschemawrapper.VSchemaWrapper{ - V: loadSchema(s.T(), "vschemas/schema.json", true), - Keyspace: &vindexes.Keyspace{ - Name: "user", - Sharded: true, - }, - Env: vtenv.NewTestEnv(), + + env := vtenv.NewTestEnv() + vschema := loadSchema(s.T(), "vschemas/schema.json", true) + vw, err := vschemawrapper.NewVschemaWrapper(env, vschema, TestBuilder) + require.NoError(s.T(), err) + + vw.Vcursor.SetTarget("user") + vw.Keyspace = &vindexes.Keyspace{ + Name: "user", + Sharded: true, } - s.testFile("onecase.json", vschema, false) + s.testFile("onecase.json", vw, false) } func (s *planTestSuite) TestOneWithTPCHVSchema() { reset := operators.EnableDebugPrinting() defer reset() - vschema := &vschemawrapper.VSchemaWrapper{ - V: loadSchema(s.T(), "vschemas/tpch_schema.json", true), - SysVarEnabled: true, - Env: vtenv.NewTestEnv(), - } - s.testFile("onecase.json", vschema, false) + env := vtenv.NewTestEnv() + vschema := loadSchema(s.T(), "vschemas/schema.json", true) + vw, err := vschemawrapper.NewVschemaWrapper(env, vschema, TestBuilder) + require.NoError(s.T(), err) + + s.testFile("onecase.json", vw, false) } func (s *planTestSuite) TestOneWith57Version() { @@ -390,52 +392,47 @@ func (s *planTestSuite) TestOneWith57Version() { MySQLServerVersion: "5.7.9", }) require.NoError(s.T(), err) - vschema := &vschemawrapper.VSchemaWrapper{ - V: loadSchema(s.T(), "vschemas/schema.json", true), - Env: env, - } + vschema := loadSchema(s.T(), "vschemas/schema.json", true) + vw, err := vschemawrapper.NewVschemaWrapper(env, vschema, TestBuilder) + require.NoError(s.T(), err) - s.testFile("onecase.json", vschema, false) + s.testFile("onecase.json", vw, false) } func (s *planTestSuite) TestRubyOnRailsQueries() { - vschemaWrapper := &vschemawrapper.VSchemaWrapper{ - V: loadSchema(s.T(), "vschemas/rails_schema.json", true), - SysVarEnabled: true, - Env: vtenv.NewTestEnv(), - } + env := vtenv.NewTestEnv() + vschema := loadSchema(s.T(), "vschemas/rails_schema.json", true) + vw, err := vschemawrapper.NewVschemaWrapper(env, vschema, TestBuilder) + require.NoError(s.T(), err) - s.testFile("rails_cases.json", vschemaWrapper, false) + s.testFile("rails_cases.json", vw, false) } func (s *planTestSuite) TestOLTP() { - vschemaWrapper := &vschemawrapper.VSchemaWrapper{ - V: loadSchema(s.T(), "vschemas/oltp_schema.json", true), - SysVarEnabled: true, - Env: vtenv.NewTestEnv(), - } + env := vtenv.NewTestEnv() + vschema := loadSchema(s.T(), "vschemas/oltp_schema.json", true) + vw, err := vschemawrapper.NewVschemaWrapper(env, vschema, TestBuilder) + require.NoError(s.T(), err) - s.testFile("oltp_cases.json", vschemaWrapper, false) + s.testFile("oltp_cases.json", vw, false) } func (s *planTestSuite) TestTPCC() { - vschemaWrapper := &vschemawrapper.VSchemaWrapper{ - V: loadSchema(s.T(), "vschemas/tpcc_schema.json", true), - SysVarEnabled: true, - Env: vtenv.NewTestEnv(), - } + env := vtenv.NewTestEnv() + vschema := loadSchema(s.T(), "vschemas/tpcc_schema.json", true) + vw, err := vschemawrapper.NewVschemaWrapper(env, vschema, TestBuilder) + require.NoError(s.T(), err) - s.testFile("tpcc_cases.json", vschemaWrapper, false) + s.testFile("tpcc_cases.json", vw, false) } func (s *planTestSuite) TestTPCH() { - vschemaWrapper := &vschemawrapper.VSchemaWrapper{ - V: loadSchema(s.T(), "vschemas/tpch_schema.json", true), - SysVarEnabled: true, - Env: vtenv.NewTestEnv(), - } + env := vtenv.NewTestEnv() + vschema := loadSchema(s.T(), "vschemas/tpch_schema.json", true) + vw, err := vschemawrapper.NewVschemaWrapper(env, vschema, TestBuilder) + require.NoError(s.T(), err) - s.testFile("tpch_cases.json", vschemaWrapper, false) + s.testFile("tpch_cases.json", vw, false) } func BenchmarkOLTP(b *testing.B) { @@ -451,15 +448,14 @@ func BenchmarkTPCH(b *testing.B) { } func benchmarkWorkload(b *testing.B, name string) { - vschemaWrapper := &vschemawrapper.VSchemaWrapper{ - V: loadSchema(b, "vschemas/"+name+"_schema.json", true), - SysVarEnabled: true, - Env: vtenv.NewTestEnv(), - } + env := vtenv.NewTestEnv() + vschema := loadSchema(b, "vschemas/"+name+"_schema.json", true) + vw, err := vschemawrapper.NewVschemaWrapper(env, vschema, TestBuilder) + require.NoError(b, err) testCases := readJSONTests(name + "_cases.json") b.ResetTimer() - benchmarkPlanner(b, Gen4, testCases, vschemaWrapper) + benchmarkPlanner(b, Gen4, testCases, vw) } func (s *planTestSuite) TestBypassPlanningShardTargetFromFile() { @@ -478,35 +474,33 @@ func (s *planTestSuite) TestBypassPlanningShardTargetFromFile() { } func (s *planTestSuite) TestBypassPlanningKeyrangeTargetFromFile() { + env := vtenv.NewTestEnv() + vschema := loadSchema(s.T(), "vschemas/schema.json", true) + vw, err := vschemawrapper.NewVschemaWrapper(env, vschema, TestBuilder) + require.NoError(s.T(), err) + keyRange, _ := key.ParseShardingSpec("-") + vw.Dest = key.DestinationExactKeyRange{KeyRange: keyRange[0]} - vschema := &vschemawrapper.VSchemaWrapper{ - V: loadSchema(s.T(), "vschemas/schema.json", true), - Keyspace: &vindexes.Keyspace{ - Name: "main", - Sharded: false, - }, - TabletType_: topodatapb.TabletType_PRIMARY, - Dest: key.DestinationExactKeyRange{KeyRange: keyRange[0]}, - Env: vtenv.NewTestEnv(), - } + vw.Vcursor.SetTarget("main") + vw.Keyspace = &vindexes.Keyspace{Name: "main"} - s.testFile("bypass_keyrange_cases.json", vschema, false) + s.testFile("bypass_keyrange_cases.json", vw, false) } func (s *planTestSuite) TestWithDefaultKeyspaceFromFile() { ctx, cancel := context.WithCancel(context.Background()) defer cancel() + // We are testing this separately so we can set a default keyspace - vschema := &vschemawrapper.VSchemaWrapper{ - V: loadSchema(s.T(), "vschemas/schema.json", true), - Keyspace: &vindexes.Keyspace{ - Name: "main", - Sharded: false, - }, - TabletType_: topodatapb.TabletType_PRIMARY, - Env: vtenv.NewTestEnv(), - } + env := vtenv.NewTestEnv() + vschema := loadSchema(s.T(), "vschemas/schema.json", true) + vw, err := vschemawrapper.NewVschemaWrapper(env, vschema, TestBuilder) + require.NoError(s.T(), err) + + vw.Vcursor.SetTarget("main") + vw.Keyspace = &vindexes.Keyspace{Name: "main"} + ts := memorytopo.NewServer(ctx, "cell1") ts.CreateKeyspace(ctx, "main", &topodatapb.Keyspace{}) ts.CreateKeyspace(ctx, "user", &topodatapb.Keyspace{}) @@ -521,97 +515,92 @@ func (s *planTestSuite) TestWithDefaultKeyspaceFromFile() { }) require.True(s.T(), created) - s.testFile("alterVschema_cases.json", vschema, false) - s.testFile("ddl_cases.json", vschema, false) - s.testFile("migration_cases.json", vschema, false) - s.testFile("flush_cases.json", vschema, false) - s.testFile("show_cases.json", vschema, false) - s.testFile("call_cases.json", vschema, false) + s.testFile("alterVschema_cases.json", vw, false) + s.testFile("ddl_cases.json", vw, false) + s.testFile("migration_cases.json", vw, false) + s.testFile("flush_cases.json", vw, false) + s.testFile("show_cases.json", vw, false) + s.testFile("call_cases.json", vw, false) } func (s *planTestSuite) TestWithDefaultKeyspaceFromFileSharded() { // We are testing this separately so we can set a default keyspace - vschema := &vschemawrapper.VSchemaWrapper{ - V: loadSchema(s.T(), "vschemas/schema.json", true), - Keyspace: &vindexes.Keyspace{ - Name: "second_user", - Sharded: true, - }, - TabletType_: topodatapb.TabletType_PRIMARY, - Env: vtenv.NewTestEnv(), + env := vtenv.NewTestEnv() + vschema := loadSchema(s.T(), "vschemas/schema.json", true) + vw, err := vschemawrapper.NewVschemaWrapper(env, vschema, TestBuilder) + require.NoError(s.T(), err) + + vw.Vcursor.SetTarget("second_user") + vw.Keyspace = &vindexes.Keyspace{ + Name: "second_user", + Sharded: true, } - s.testFile("select_cases_with_default.json", vschema, false) + s.testFile("select_cases_with_default.json", vw, false) } func (s *planTestSuite) TestWithUserDefaultKeyspaceFromFileSharded() { // We are testing this separately so we can set a default keyspace - vschema := &vschemawrapper.VSchemaWrapper{ - V: loadSchema(s.T(), "vschemas/schema.json", true), - Keyspace: &vindexes.Keyspace{ - Name: "user", - Sharded: true, - }, - TabletType_: topodatapb.TabletType_PRIMARY, - Env: vtenv.NewTestEnv(), + env := vtenv.NewTestEnv() + vschema := loadSchema(s.T(), "vschemas/schema.json", true) + vw, err := vschemawrapper.NewVschemaWrapper(env, vschema, TestBuilder) + require.NoError(s.T(), err) + + vw.Vcursor.SetTarget("user") + vw.Keyspace = &vindexes.Keyspace{ + Name: "user", + Sharded: true, } - s.testFile("select_cases_with_user_as_default.json", vschema, false) - s.testFile("dml_cases_with_user_as_default.json", vschema, false) + s.testFile("select_cases_with_user_as_default.json", vw, false) + s.testFile("dml_cases_with_user_as_default.json", vw, false) } func (s *planTestSuite) TestWithSystemSchemaAsDefaultKeyspace() { // We are testing this separately so we can set a default keyspace - vschema := &vschemawrapper.VSchemaWrapper{ - V: loadSchema(s.T(), "vschemas/schema.json", true), - Keyspace: &vindexes.Keyspace{Name: "information_schema"}, - TabletType_: topodatapb.TabletType_PRIMARY, - Env: vtenv.NewTestEnv(), - } + env := vtenv.NewTestEnv() + vschema := loadSchema(s.T(), "vschemas/schema.json", true) + vw, err := vschemawrapper.NewVschemaWrapper(env, vschema, TestBuilder) + require.NoError(s.T(), err) + + vw.Keyspace = &vindexes.Keyspace{Name: "information_schema"} - s.testFile("sysschema_default.json", vschema, false) + s.testFile("sysschema_default.json", vw, false) } func (s *planTestSuite) TestOtherPlanningFromFile() { // We are testing this separately so we can set a default keyspace - vschema := &vschemawrapper.VSchemaWrapper{ - V: loadSchema(s.T(), "vschemas/schema.json", true), - Keyspace: &vindexes.Keyspace{ - Name: "main", - Sharded: false, - }, - TabletType_: topodatapb.TabletType_PRIMARY, - Env: vtenv.NewTestEnv(), - } + env := vtenv.NewTestEnv() + vschema := loadSchema(s.T(), "vschemas/schema.json", true) + vw, err := vschemawrapper.NewVschemaWrapper(env, vschema, TestBuilder) + require.NoError(s.T(), err) - s.testFile("other_read_cases.json", vschema, false) - s.testFile("other_admin_cases.json", vschema, false) + vw.Vcursor.SetTarget("main") + vw.Keyspace = &vindexes.Keyspace{Name: "main"} + + s.testFile("other_read_cases.json", vw, false) + s.testFile("other_admin_cases.json", vw, false) } func (s *planTestSuite) TestMirrorPlanning() { - vschema := &vschemawrapper.VSchemaWrapper{ - V: loadSchema(s.T(), "vschemas/mirror_schema.json", true), - TabletType_: topodatapb.TabletType_PRIMARY, - SysVarEnabled: true, - TestBuilder: TestBuilder, - Env: vtenv.NewTestEnv(), - } + env := vtenv.NewTestEnv() + vschema := loadSchema(s.T(), "vschemas/mirror_schema.json", true) + vw, err := vschemawrapper.NewVschemaWrapper(env, vschema, TestBuilder) + require.NoError(s.T(), err) - s.testFile("mirror_cases.json", vschema, false) + s.testFile("mirror_cases.json", vw, false) } func (s *planTestSuite) TestOneMirror() { reset := operators.EnableDebugPrinting() defer reset() - vschema := &vschemawrapper.VSchemaWrapper{ - V: loadSchema(s.T(), "vschemas/mirror_schema.json", true), - TabletType_: topodatapb.TabletType_PRIMARY, - SysVarEnabled: true, - TestBuilder: TestBuilder, - Env: vtenv.NewTestEnv(), - } - s.testFile("onecase.json", vschema, false) + env := vtenv.NewTestEnv() + vschema := loadSchema(s.T(), "vschemas/schema.json", true) + vw, err := vschemawrapper.NewVschemaWrapper(env, vschema, TestBuilder) + require.NoError(s.T(), err) + + s.testFile("onecase.json", vw, false) } func loadSchema(t testing.TB, filename string, setCollation bool) *vindexes.VSchema { @@ -784,30 +773,29 @@ func locateFile(name string) string { var benchMarkFiles = []string{"from_cases.json", "filter_cases.json", "large_cases.json", "aggr_cases.json", "select_cases.json", "union_cases.json"} func BenchmarkPlanner(b *testing.B) { - vschema := &vschemawrapper.VSchemaWrapper{ - V: loadSchema(b, "vschemas/schema.json", true), - SysVarEnabled: true, - Env: vtenv.NewTestEnv(), - } + env := vtenv.NewTestEnv() + vschema := loadSchema(b, "vschemas/schema.json", true) + vw, err := vschemawrapper.NewVschemaWrapper(env, vschema, TestBuilder) + require.NoError(b, err) + for _, filename := range benchMarkFiles { testCases := readJSONTests(filename) b.Run(filename+"-gen4", func(b *testing.B) { - benchmarkPlanner(b, Gen4, testCases, vschema) + benchmarkPlanner(b, Gen4, testCases, vw) }) } } func BenchmarkSemAnalysis(b *testing.B) { - vschema := &vschemawrapper.VSchemaWrapper{ - V: loadSchema(b, "vschemas/schema.json", true), - SysVarEnabled: true, - Env: vtenv.NewTestEnv(), - } + env := vtenv.NewTestEnv() + vschema := loadSchema(b, "vschemas/schema.json", true) + vw, err := vschemawrapper.NewVschemaWrapper(env, vschema, TestBuilder) + require.NoError(b, err) for i := 0; i < b.N; i++ { for _, filename := range benchMarkFiles { for _, tc := range readJSONTests(filename) { - exerciseAnalyzer(tc.Query, vschema.CurrentDb(), vschema) + exerciseAnalyzer(tc.Query, vw.CurrentDb(), vw) } } } @@ -832,12 +820,10 @@ func exerciseAnalyzer(query, database string, s semantics.SchemaInformation) { } func BenchmarkSelectVsDML(b *testing.B) { - vschema := &vschemawrapper.VSchemaWrapper{ - V: loadSchema(b, "vschemas/schema.json", true), - SysVarEnabled: true, - Version: Gen4, - Env: vtenv.NewTestEnv(), - } + env := vtenv.NewTestEnv() + vschema := loadSchema(b, "vschemas/schema.json", true) + vw, err := vschemawrapper.NewVschemaWrapper(env, vschema, TestBuilder) + require.NoError(b, err) dmlCases := readJSONTests("dml_cases.json") selectCases := readJSONTests("select_cases.json") @@ -851,40 +837,33 @@ func BenchmarkSelectVsDML(b *testing.B) { }) b.Run("DML (random sample, N=32)", func(b *testing.B) { - benchmarkPlanner(b, Gen4, dmlCases[:32], vschema) + benchmarkPlanner(b, Gen4, dmlCases[:32], vw) }) b.Run("Select (random sample, N=32)", func(b *testing.B) { - benchmarkPlanner(b, Gen4, selectCases[:32], vschema) + benchmarkPlanner(b, Gen4, selectCases[:32], vw) }) } func BenchmarkBaselineVsMirrored(b *testing.B) { + env := vtenv.NewTestEnv() baseline := loadSchema(b, "vschemas/mirror_schema.json", true) baseline.MirrorRules = map[string]*vindexes.MirrorRule{} - baselineVschema := &vschemawrapper.VSchemaWrapper{ - V: baseline, - SysVarEnabled: true, - Version: Gen4, - Env: vtenv.NewTestEnv(), - } + bvw, err := vschemawrapper.NewVschemaWrapper(env, baseline, TestBuilder) + require.NoError(b, err) mirroredSchema := loadSchema(b, "vschemas/mirror_schema.json", true) - mirroredVschema := &vschemawrapper.VSchemaWrapper{ - V: mirroredSchema, - SysVarEnabled: true, - Version: Gen4, - Env: vtenv.NewTestEnv(), - } + mvw, err := vschemawrapper.NewVschemaWrapper(env, mirroredSchema, TestBuilder) + require.NoError(b, err) cases := readJSONTests("mirror_cases.json") b.Run("Baseline", func(b *testing.B) { - benchmarkPlanner(b, Gen4, cases, baselineVschema) + benchmarkPlanner(b, Gen4, cases, bvw) }) b.Run("Mirrored", func(b *testing.B) { - benchmarkPlanner(b, Gen4, cases, mirroredVschema) + benchmarkPlanner(b, Gen4, cases, mvw) }) } diff --git a/go/vt/vtgate/planbuilder/plancontext/planning_context.go b/go/vt/vtgate/planbuilder/plancontext/planning_context.go index 607ca83aa31..016f5c877cf 100644 --- a/go/vt/vtgate/planbuilder/plancontext/planning_context.go +++ b/go/vt/vtgate/planbuilder/plancontext/planning_context.go @@ -91,7 +91,7 @@ func CreatePlanningContext(stmt sqlparser.Statement, version querypb.ExecuteOptions_PlannerVersion, ) (*PlanningContext, error) { ksName := "" - if ks, _ := vschema.DefaultKeyspace(); ks != nil { + if ks, _ := vschema.SelectedKeyspace(); ks != nil { ksName = ks.Name } diff --git a/go/vt/vtgate/planbuilder/plancontext/planning_context_test.go b/go/vt/vtgate/planbuilder/plancontext/planning_context_test.go index d7315f376b6..e5e96b0a4be 100644 --- a/go/vt/vtgate/planbuilder/plancontext/planning_context_test.go +++ b/go/vt/vtgate/planbuilder/plancontext/planning_context_test.go @@ -201,7 +201,7 @@ func (v *vschema) FindTableOrVindex(tablename sqlparser.TableName) (*vindexes.Ta panic("implement me") } -func (v *vschema) DefaultKeyspace() (*vindexes.Keyspace, error) { +func (v *vschema) SelectedKeyspace() (*vindexes.Keyspace, error) { // TODO implement me panic("implement me") } diff --git a/go/vt/vtgate/planbuilder/plancontext/vschema.go b/go/vt/vtgate/planbuilder/plancontext/vschema.go index 6e92ad0d83b..b4560424718 100644 --- a/go/vt/vtgate/planbuilder/plancontext/vschema.go +++ b/go/vt/vtgate/planbuilder/plancontext/vschema.go @@ -27,7 +27,9 @@ type VSchema interface { FindTable(tablename sqlparser.TableName) (*vindexes.Table, string, topodatapb.TabletType, key.Destination, error) FindView(name sqlparser.TableName) sqlparser.SelectStatement FindTableOrVindex(tablename sqlparser.TableName) (*vindexes.Table, vindexes.Vindex, string, topodatapb.TabletType, key.Destination, error) - DefaultKeyspace() (*vindexes.Keyspace, error) + + // SelectedKeyspace returns the current keyspace if set, otherwise returns an error + SelectedKeyspace() (*vindexes.Keyspace, error) TargetString() string Destination() key.Destination TabletType() topodatapb.TabletType diff --git a/go/vt/vtgate/planbuilder/select.go b/go/vt/vtgate/planbuilder/select.go index 9cc1c8efe06..409343f2760 100644 --- a/go/vt/vtgate/planbuilder/select.go +++ b/go/vt/vtgate/planbuilder/select.go @@ -46,7 +46,7 @@ func gen4SelectStmtPlanner( } if p != nil { used := "dual" - keyspace, ksErr := vschema.DefaultKeyspace() + keyspace, ksErr := vschema.SelectedKeyspace() if ksErr == nil { // we are just getting the ks to log the correct table use. // no need to fail this if we can't find the default keyspace @@ -101,7 +101,7 @@ func gen4SelectStmtPlanner( func gen4planSQLCalcFoundRows(vschema plancontext.VSchema, sel *sqlparser.Select, query string, reservedVars *sqlparser.ReservedVars) (*planResult, error) { ksName := "" - if ks, _ := vschema.DefaultKeyspace(); ks != nil { + if ks, _ := vschema.SelectedKeyspace(); ks != nil { ksName = ks.Name } semTable, err := semantics.Analyze(sel, ksName, vschema) diff --git a/go/vt/vtgate/planbuilder/show.go b/go/vt/vtgate/planbuilder/show.go index 82035adaa87..40cf7b2411f 100644 --- a/go/vt/vtgate/planbuilder/show.go +++ b/go/vt/vtgate/planbuilder/show.go @@ -676,7 +676,7 @@ func buildVschemaKeyspacesPlan(vschema plancontext.VSchema) (engine.Primitive, e func buildVschemaTablesPlan(vschema plancontext.VSchema) (engine.Primitive, error) { vs := vschema.GetVSchema() - ks, err := vschema.DefaultKeyspace() + ks, err := vschema.SelectedKeyspace() if err != nil { return nil, err } diff --git a/go/vt/vtgate/planbuilder/show_test.go b/go/vt/vtgate/planbuilder/show_test.go index bfdb9a623a0..c3651aaa1cd 100644 --- a/go/vt/vtgate/planbuilder/show_test.go +++ b/go/vt/vtgate/planbuilder/show_test.go @@ -32,10 +32,13 @@ import ( ) func TestBuildDBPlan(t *testing.T) { - vschema := &vschemawrapper.VSchemaWrapper{ - Keyspace: &vindexes.Keyspace{Name: "main"}, - Env: vtenv.NewTestEnv(), - } + env := vtenv.NewTestEnv() + vschema := loadSchema(t, "vschemas/schema.json", true) + vw, err := vschemawrapper.NewVschemaWrapper(env, vschema, TestBuilder) + require.NoError(t, err) + + vw.Vcursor.SetTarget("main") + vw.Keyspace = &vindexes.Keyspace{Name: "main"} testCases := []struct { query string @@ -54,7 +57,7 @@ func TestBuildDBPlan(t *testing.T) { require.NoError(t, err) show := parserOut.(*sqlparser.Show) - primitive, err := buildDBPlan(show.Internal.(*sqlparser.ShowBasic), vschema) + primitive, err := buildDBPlan(show.Internal.(*sqlparser.ShowBasic), vw) require.NoError(t, err) result, err := primitive.TryExecute(context.Background(), nil, nil, false) diff --git a/go/vt/vtgate/planbuilder/simplifier_test.go b/go/vt/vtgate/planbuilder/simplifier_test.go index 305c18896e3..dce21b3e175 100644 --- a/go/vt/vtgate/planbuilder/simplifier_test.go +++ b/go/vt/vtgate/planbuilder/simplifier_test.go @@ -38,21 +38,21 @@ func TestSimplifyBuggyQuery(t *testing.T) { query := "select distinct count(distinct a), count(distinct 4) from user left join unsharded on 0 limit 5" // select 0 from unsharded union select 0 from `user` union select 0 from unsharded // select 0 from unsharded union (select 0 from `user` union select 0 from unsharded) - vschema := &vschemawrapper.VSchemaWrapper{ - V: loadSchema(t, "vschemas/schema.json", true), - Version: Gen4, - Env: vtenv.NewTestEnv(), - } + env := vtenv.NewTestEnv() + vschema := loadSchema(t, "vschemas/schema.json", true) + vw, err := vschemawrapper.NewVschemaWrapper(env, vschema, TestBuilder) + require.NoError(t, err) + stmt, reserved, err := sqlparser.NewTestParser().Parse2(query) require.NoError(t, err) - rewritten, _ := sqlparser.RewriteAST(sqlparser.Clone(stmt), vschema.CurrentDb(), sqlparser.SQLSelectLimitUnset, "", nil, nil, nil) + rewritten, _ := sqlparser.RewriteAST(sqlparser.Clone(stmt), vw.CurrentDb(), sqlparser.SQLSelectLimitUnset, "", nil, nil, nil) reservedVars := sqlparser.NewReservedVars("vtg", reserved) simplified := simplifier.SimplifyStatement( stmt.(sqlparser.SelectStatement), - vschema.CurrentDb(), - vschema, - keepSameError(query, reservedVars, vschema, rewritten.BindVarNeeds), + vw.CurrentDb(), + vw, + keepSameError(query, reservedVars, vw, rewritten.BindVarNeeds), ) fmt.Println(sqlparser.String(simplified)) @@ -61,21 +61,22 @@ func TestSimplifyBuggyQuery(t *testing.T) { func TestSimplifyPanic(t *testing.T) { t.Skip("not needed to run") query := "(select id from unsharded union select id from unsharded_auto) union (select id from unsharded_auto union select name from unsharded)" - vschema := &vschemawrapper.VSchemaWrapper{ - V: loadSchema(t, "vschemas/schema.json", true), - Version: Gen4, - Env: vtenv.NewTestEnv(), - } + + env := vtenv.NewTestEnv() + vschema := loadSchema(t, "vschemas/schema.json", true) + vw, err := vschemawrapper.NewVschemaWrapper(env, vschema, TestBuilder) + require.NoError(t, err) + stmt, reserved, err := sqlparser.NewTestParser().Parse2(query) require.NoError(t, err) - rewritten, _ := sqlparser.RewriteAST(sqlparser.Clone(stmt), vschema.CurrentDb(), sqlparser.SQLSelectLimitUnset, "", nil, nil, nil) + rewritten, _ := sqlparser.RewriteAST(sqlparser.Clone(stmt), vw.CurrentDb(), sqlparser.SQLSelectLimitUnset, "", nil, nil, nil) reservedVars := sqlparser.NewReservedVars("vtg", reserved) simplified := simplifier.SimplifyStatement( stmt.(sqlparser.SelectStatement), - vschema.CurrentDb(), - vschema, - keepPanicking(query, reservedVars, vschema, rewritten.BindVarNeeds), + vw.CurrentDb(), + vw, + keepPanicking(query, reservedVars, vw, rewritten.BindVarNeeds), ) fmt.Println(sqlparser.String(simplified)) @@ -83,11 +84,11 @@ func TestSimplifyPanic(t *testing.T) { func TestUnsupportedFile(t *testing.T) { t.Skip("run manually to see if any queries can be simplified") - vschema := &vschemawrapper.VSchemaWrapper{ - V: loadSchema(t, "vschemas/schema.json", true), - Version: Gen4, - Env: vtenv.NewTestEnv(), - } + env := vtenv.NewTestEnv() + vschema := loadSchema(t, "vschemas/schema.json", true) + vw, err := vschemawrapper.NewVschemaWrapper(env, vschema, TestBuilder) + require.NoError(t, err) + fmt.Println(vschema) for _, tcase := range readJSONTests("unsupported_cases.txt") { t.Run(tcase.Query, func(t *testing.T) { @@ -99,11 +100,10 @@ func TestUnsupportedFile(t *testing.T) { t.Skip() return } - rewritten, err := sqlparser.RewriteAST(stmt, vschema.CurrentDb(), sqlparser.SQLSelectLimitUnset, "", nil, nil, nil) + rewritten, err := sqlparser.RewriteAST(stmt, vw.CurrentDb(), sqlparser.SQLSelectLimitUnset, "", nil, nil, nil) if err != nil { t.Skip() } - vschema.CurrentDb() reservedVars := sqlparser.NewReservedVars("vtg", reserved) ast := rewritten.AST @@ -111,9 +111,9 @@ func TestUnsupportedFile(t *testing.T) { stmt, _, _ = sqlparser.NewTestParser().Parse2(tcase.Query) simplified := simplifier.SimplifyStatement( stmt.(sqlparser.SelectStatement), - vschema.CurrentDb(), - vschema, - keepSameError(tcase.Query, reservedVars, vschema, rewritten.BindVarNeeds), + vw.CurrentDb(), + vw, + keepSameError(tcase.Query, reservedVars, vw, rewritten.BindVarNeeds), ) if simplified == nil { @@ -135,12 +135,12 @@ func keepSameError(query string, reservedVars *sqlparser.ReservedVars, vschema * } rewritten, _ := sqlparser.RewriteAST(stmt, vschema.CurrentDb(), sqlparser.SQLSelectLimitUnset, "", nil, nil, nil) ast := rewritten.AST - _, expected := BuildFromStmt(context.Background(), query, ast, reservedVars, vschema, rewritten.BindVarNeeds, true, true) + _, expected := BuildFromStmt(context.Background(), query, ast, reservedVars, vschema, rewritten.BindVarNeeds, staticConfig{}) if expected == nil { panic("query does not fail to plan") } return func(statement sqlparser.SelectStatement) bool { - _, myErr := BuildFromStmt(context.Background(), query, statement, reservedVars, vschema, needs, true, true) + _, myErr := BuildFromStmt(context.Background(), query, statement, reservedVars, vschema, needs, staticConfig{}) if myErr == nil { return false } @@ -162,7 +162,7 @@ func keepPanicking(query string, reservedVars *sqlparser.ReservedVars, vschema * } }() log.Errorf("trying %s", sqlparser.String(statement)) - _, _ = BuildFromStmt(context.Background(), query, statement, reservedVars, vschema, needs, true, true) + _, _ = BuildFromStmt(context.Background(), query, statement, reservedVars, vschema, needs, staticConfig{}) log.Errorf("did not panic") return false diff --git a/go/vt/vtgate/planbuilder/vexplain.go b/go/vt/vtgate/planbuilder/vexplain.go index f66af7bfc33..7aed1e48884 100644 --- a/go/vt/vtgate/planbuilder/vexplain.go +++ b/go/vt/vtgate/planbuilder/vexplain.go @@ -26,6 +26,7 @@ import ( vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vterrors" + "vitess.io/vitess/go/vt/vtgate/dynamicconfig" "vitess.io/vitess/go/vt/vtgate/engine" "vitess.io/vitess/go/vt/vtgate/planbuilder/operators" "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" @@ -37,15 +38,15 @@ func buildVExplainPlan( vexplainStmt *sqlparser.VExplainStmt, reservedVars *sqlparser.ReservedVars, vschema plancontext.VSchema, - enableOnlineDDL, enableDirectDDL bool, + cfg dynamicconfig.DDL, ) (*planResult, error) { switch vexplainStmt.Type { case sqlparser.QueriesVExplainType, sqlparser.AllVExplainType: - return buildVExplainLoggingPlan(ctx, vexplainStmt, reservedVars, vschema, enableOnlineDDL, enableDirectDDL) + return buildVExplainLoggingPlan(ctx, vexplainStmt, reservedVars, vschema, cfg) case sqlparser.PlanVExplainType: - return buildVExplainVtgatePlan(ctx, vexplainStmt.Statement, reservedVars, vschema, enableOnlineDDL, enableDirectDDL) + return buildVExplainVtgatePlan(ctx, vexplainStmt.Statement, reservedVars, vschema, cfg) case sqlparser.TraceVExplainType: - return buildVExplainTracePlan(ctx, vexplainStmt.Statement, reservedVars, vschema, enableOnlineDDL, enableDirectDDL) + return buildVExplainTracePlan(ctx, vexplainStmt.Statement, reservedVars, vschema, cfg) case sqlparser.KeysVExplainType: return buildVExplainKeysPlan(vexplainStmt.Statement, vschema) } @@ -92,8 +93,8 @@ func explainTabPlan(explain *sqlparser.ExplainTab, vschema plancontext.VSchema) }, singleTable(keyspace.Name, explain.Table.Name.String())), nil } -func buildVExplainVtgatePlan(ctx context.Context, explainStatement sqlparser.Statement, reservedVars *sqlparser.ReservedVars, vschema plancontext.VSchema, enableOnlineDDL, enableDirectDDL bool) (*planResult, error) { - innerInstruction, err := createInstructionFor(ctx, sqlparser.String(explainStatement), explainStatement, reservedVars, vschema, enableOnlineDDL, enableDirectDDL) +func buildVExplainVtgatePlan(ctx context.Context, explainStatement sqlparser.Statement, reservedVars *sqlparser.ReservedVars, vschema plancontext.VSchema, cfg dynamicconfig.DDL) (*planResult, error) { + innerInstruction, err := createInstructionFor(ctx, sqlparser.String(explainStatement), explainStatement, reservedVars, vschema, cfg) if err != nil { return nil, err } @@ -124,8 +125,8 @@ func buildVExplainKeysPlan(statement sqlparser.Statement, vschema plancontext.VS return getJsonResultPlan(result, "ColumnUsage") } -func buildVExplainLoggingPlan(ctx context.Context, explain *sqlparser.VExplainStmt, reservedVars *sqlparser.ReservedVars, vschema plancontext.VSchema, enableOnlineDDL, enableDirectDDL bool) (*planResult, error) { - input, err := createInstructionFor(ctx, sqlparser.String(explain.Statement), explain.Statement, reservedVars, vschema, enableOnlineDDL, enableDirectDDL) +func buildVExplainLoggingPlan(ctx context.Context, explain *sqlparser.VExplainStmt, reservedVars *sqlparser.ReservedVars, vschema plancontext.VSchema, cfg dynamicconfig.DDL) (*planResult, error) { + input, err := createInstructionFor(ctx, sqlparser.String(explain.Statement), explain.Statement, reservedVars, vschema, cfg) if err != nil { return nil, err } @@ -188,8 +189,8 @@ func explainPlan(explain *sqlparser.ExplainStmt, reservedVars *sqlparser.Reserve }, tables...), nil } -func buildVExplainTracePlan(ctx context.Context, explainStatement sqlparser.Statement, reservedVars *sqlparser.ReservedVars, vschema plancontext.VSchema, enableOnlineDDL, enableDirectDDL bool) (*planResult, error) { - innerInstruction, err := createInstructionFor(ctx, sqlparser.String(explainStatement), explainStatement, reservedVars, vschema, enableOnlineDDL, enableDirectDDL) +func buildVExplainTracePlan(ctx context.Context, explainStatement sqlparser.Statement, reservedVars *sqlparser.ReservedVars, vschema plancontext.VSchema, cfg dynamicconfig.DDL) (*planResult, error) { + innerInstruction, err := createInstructionFor(ctx, sqlparser.String(explainStatement), explainStatement, reservedVars, vschema, cfg) if err != nil { return nil, err } diff --git a/go/vt/vtgate/querylogz.go b/go/vt/vtgate/querylogz.go index 7c72e950d4a..05d301f28be 100644 --- a/go/vt/vtgate/querylogz.go +++ b/go/vt/vtgate/querylogz.go @@ -20,15 +20,15 @@ import ( "net/http" "strconv" "strings" - "text/template" "time" - "vitess.io/vitess/go/vt/vtgate/logstats" + "github.com/google/safehtml/template" "vitess.io/vitess/go/acl" "vitess.io/vitess/go/vt/log" "vitess.io/vitess/go/vt/logz" "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/vtgate/logstats" ) var ( diff --git a/go/vt/vtgate/querylogz_test.go b/go/vt/vtgate/querylogz_test.go index 3cecb983b3f..9236b2ac840 100644 --- a/go/vt/vtgate/querylogz_test.go +++ b/go/vt/vtgate/querylogz_test.go @@ -35,7 +35,7 @@ import ( func TestQuerylogzHandlerFormatting(t *testing.T) { req, _ := http.NewRequest("GET", "/querylogz?timeout=10&limit=1", nil) - logStats := logstats.NewLogStats(context.Background(), "Execute", "select name from test_table limit 1000", "suuid", nil) + logStats := logstats.NewLogStats(context.Background(), "Execute", "select name, 'inject ' from test_table limit 1000", "suuid", nil) logStats.StmtType = "select" logStats.RowsAffected = 1000 logStats.ShardQueries = 1 @@ -64,7 +64,7 @@ func TestQuerylogzHandlerFormatting(t *testing.T) { `0.002`, `0.003`, `select`, - `select name from test_table limit 1000`, + regexp.QuoteMeta(`select name,​ 'inject <script>alert()​;</script>' from test_table limit 1000`), `1`, `1000`, ``, @@ -94,7 +94,7 @@ func TestQuerylogzHandlerFormatting(t *testing.T) { `0.002`, `0.003`, `select`, - `select name from test_table limit 1000`, + regexp.QuoteMeta(`select name,​ 'inject <script>alert()​;</script>' from test_table limit 1000`), `1`, `1000`, ``, @@ -124,7 +124,7 @@ func TestQuerylogzHandlerFormatting(t *testing.T) { `0.002`, `0.003`, `select`, - `select name from test_table limit 1000`, + regexp.QuoteMeta(`select name,​ 'inject <script>alert()​;</script>' from test_table limit 1000`), `1`, `1000`, ``, diff --git a/go/vt/vtgate/safe_session_test.go b/go/vt/vtgate/safe_session_test.go deleted file mode 100644 index ce681fe7fd3..00000000000 --- a/go/vt/vtgate/safe_session_test.go +++ /dev/null @@ -1,100 +0,0 @@ -/* -Copyright 2020 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 vtgate - -import ( - "reflect" - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - querypb "vitess.io/vitess/go/vt/proto/query" - topodatapb "vitess.io/vitess/go/vt/proto/topodata" - vtgatepb "vitess.io/vitess/go/vt/proto/vtgate" -) - -func TestFailToMultiShardWhenSetToSingleDb(t *testing.T) { - session := NewSafeSession(&vtgatepb.Session{ - InTransaction: true, TransactionMode: vtgatepb.TransactionMode_SINGLE, - }) - - sess0 := &vtgatepb.Session_ShardSession{ - Target: &querypb.Target{Keyspace: "keyspace", Shard: "0"}, - TabletAlias: &topodatapb.TabletAlias{Cell: "cell", Uid: 0}, - TransactionId: 1, - } - sess1 := &vtgatepb.Session_ShardSession{ - Target: &querypb.Target{Keyspace: "keyspace", Shard: "1"}, - TabletAlias: &topodatapb.TabletAlias{Cell: "cell", Uid: 1}, - TransactionId: 1, - } - - err := session.AppendOrUpdate(sess0, vtgatepb.TransactionMode_SINGLE) - require.NoError(t, err) - err = session.AppendOrUpdate(sess1, vtgatepb.TransactionMode_SINGLE) - require.Error(t, err) -} - -func TestPrequeries(t *testing.T) { - session := NewSafeSession(&vtgatepb.Session{ - SystemVariables: map[string]string{ - "s1": "'apa'", - "s2": "42", - }, - }) - - want := []string{"set s1 = 'apa', s2 = 42"} - preQueries := session.SetPreQueries() - - if !reflect.DeepEqual(want, preQueries) { - t.Errorf("got %v but wanted %v", preQueries, want) - } -} - -func TestTimeZone(t *testing.T) { - testCases := []struct { - tz string - want string - }{ - { - tz: "'Europe/Amsterdam'", - want: "Europe/Amsterdam", - }, - { - tz: "'+02:00'", - want: "UTC+02:00", - }, - { - tz: "foo", - want: (*time.Location)(nil).String(), - }, - } - - for _, tc := range testCases { - t.Run(tc.tz, func(t *testing.T) { - session := NewSafeSession(&vtgatepb.Session{ - SystemVariables: map[string]string{ - "time_zone": tc.tz, - }, - }) - - assert.Equal(t, tc.want, session.TimeZone().String()) - }) - } -} diff --git a/go/vt/vtgate/scatter_conn.go b/go/vt/vtgate/scatter_conn.go index f7db598127e..6e2cf9ad8ba 100644 --- a/go/vt/vtgate/scatter_conn.go +++ b/go/vt/vtgate/scatter_conn.go @@ -24,26 +24,25 @@ import ( "sync/atomic" "time" - "vitess.io/vitess/go/mysql/sqlerror" - "vitess.io/vitess/go/vt/sqlparser" - "google.golang.org/protobuf/proto" + "vitess.io/vitess/go/mysql/sqlerror" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/stats" "vitess.io/vitess/go/vt/concurrency" "vitess.io/vitess/go/vt/discovery" "vitess.io/vitess/go/vt/log" + querypb "vitess.io/vitess/go/vt/proto/query" + topodatapb "vitess.io/vitess/go/vt/proto/topodata" + vtgatepb "vitess.io/vitess/go/vt/proto/vtgate" + vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" + "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/srvtopo" "vitess.io/vitess/go/vt/topo/topoproto" "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vtgate/engine" + econtext "vitess.io/vitess/go/vt/vtgate/executorcontext" "vitess.io/vitess/go/vt/vttablet/queryservice" - - querypb "vitess.io/vitess/go/vt/proto/query" - topodatapb "vitess.io/vitess/go/vt/proto/topodata" - vtgatepb "vitess.io/vitess/go/vt/proto/vtgate" - vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" ) // ScatterConn is used for executing queries across @@ -73,13 +72,10 @@ type shardActionFunc func(rs *srvtopo.ResolvedShard, i int) error type shardActionTransactionFunc func(rs *srvtopo.ResolvedShard, i int, shardActionInfo *shardActionInfo) (*shardActionInfo, error) type ( - resultsObserver interface { - observe(*sqltypes.Result) - } nullResultsObserver struct{} ) -func (nullResultsObserver) observe(*sqltypes.Result) {} +func (nullResultsObserver) Observe(*sqltypes.Result) {} // NewScatterConn creates a new ScatterConn. func NewScatterConn(statsName string, txConn *TxConn, gw *TabletGateway) *ScatterConn { @@ -108,7 +104,7 @@ func (stc *ScatterConn) startAction(name string, target *querypb.Target) (time.T return startTime, statsKey } -func (stc *ScatterConn) endAction(startTime time.Time, allErrors *concurrency.AllErrorRecorder, statsKey []string, err *error, session *SafeSession) { +func (stc *ScatterConn) endAction(startTime time.Time, allErrors *concurrency.AllErrorRecorder, statsKey []string, err *error, session *econtext.SafeSession) { if *err != nil { allErrors.RecordError(*err) // Don't increment the error counter for duplicate @@ -152,10 +148,10 @@ func (stc *ScatterConn) ExecuteMultiShard( primitive engine.Primitive, rss []*srvtopo.ResolvedShard, queries []*querypb.BoundQuery, - session *SafeSession, + session *econtext.SafeSession, autocommit bool, ignoreMaxMemoryRows bool, - resultsObserver resultsObserver, + resultsObserver econtext.ResultsObserver, ) (qr *sqltypes.Result, errs []error) { if len(rss) != len(queries) { @@ -166,7 +162,7 @@ func (stc *ScatterConn) ExecuteMultiShard( var mu sync.Mutex qr = new(sqltypes.Result) - if session.InLockSession() && session.TriggerLockHeartBeat() { + if session.InLockSession() && triggerLockHeartBeat(session) { go stc.runLockQuery(ctx, session) } @@ -224,6 +220,7 @@ func (stc *ScatterConn) ExecuteMultiShard( retryRequest(func() { // we seem to have lost our connection. it was a reserved connection, let's try to recreate it info.actionNeeded = reserve + info.ignoreOldSession = true var state queryservice.ReservedState state, innerqr, err = qs.ReserveExecute(ctx, rs.Target, session.SetPreQueries(), queries[i].Sql, queries[i].BindVariables, 0 /*transactionId*/, opts) reservedID = state.ReservedID @@ -239,6 +236,7 @@ func (stc *ScatterConn) ExecuteMultiShard( retryRequest(func() { // we seem to have lost our connection. it was a reserved connection, let's try to recreate it info.actionNeeded = reserveBegin + info.ignoreOldSession = true var state queryservice.ReservedTransactionState state, innerqr, err = qs.ReserveBeginExecute(ctx, rs.Target, session.SetPreQueries(), session.SavePoints(), queries[i].Sql, queries[i].BindVariables, opts) transactionID = state.TransactionID @@ -260,10 +258,10 @@ func (stc *ScatterConn) ExecuteMultiShard( default: return nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "[BUG] unexpected actionNeeded on query execution: %v", info.actionNeeded) } - session.logging.log(primitive, rs.Target, rs.Gateway, queries[i].Sql, info.actionNeeded == begin || info.actionNeeded == reserveBegin, queries[i].BindVariables) + session.Log(primitive, rs.Target, rs.Gateway, queries[i].Sql, info.actionNeeded == begin || info.actionNeeded == reserveBegin, queries[i].BindVariables) // We need to new shard info irrespective of the error. - newInfo := info.updateTransactionAndReservedID(transactionID, reservedID, alias) + newInfo := info.updateTransactionAndReservedID(transactionID, reservedID, alias, innerqr) if err != nil { return newInfo, err } @@ -271,7 +269,7 @@ func (stc *ScatterConn) ExecuteMultiShard( defer mu.Unlock() if innerqr != nil { - resultsObserver.observe(innerqr) + resultsObserver.Observe(innerqr) } // Don't append more rows if row count is exceeded. @@ -289,7 +287,13 @@ func (stc *ScatterConn) ExecuteMultiShard( return qr, allErrors.GetErrors() } -func (stc *ScatterConn) runLockQuery(ctx context.Context, session *SafeSession) { +func triggerLockHeartBeat(session *econtext.SafeSession) bool { + now := time.Now().Unix() + lastHeartbeat := session.GetLockHeartbeat() + return now-lastHeartbeat >= int64(lockHeartbeatTime.Seconds()) +} + +func (stc *ScatterConn) runLockQuery(ctx context.Context, session *econtext.SafeSession) { rs := &srvtopo.ResolvedShard{Target: session.LockSession.Target, Gateway: stc.gateway} query := &querypb.BoundQuery{Sql: "select 1", BindVariables: nil} _, lockErr := stc.ExecuteLock(ctx, rs, query, session, sqlparser.IsUsedLock) @@ -298,7 +302,7 @@ func (stc *ScatterConn) runLockQuery(ctx context.Context, session *SafeSession) } } -func checkAndResetShardSession(info *shardActionInfo, err error, session *SafeSession, target *querypb.Target) reset { +func checkAndResetShardSession(info *shardActionInfo, err error, session *econtext.SafeSession, target *querypb.Target) reset { retry := none if info.reservedID != 0 && info.transactionID == 0 { if wasConnectionClosed(err) { @@ -314,7 +318,7 @@ func checkAndResetShardSession(info *shardActionInfo, err error, session *SafeSe return retry } -func getQueryService(ctx context.Context, rs *srvtopo.ResolvedShard, info *shardActionInfo, session *SafeSession, skipReset bool) (queryservice.QueryService, error) { +func getQueryService(ctx context.Context, rs *srvtopo.ResolvedShard, info *shardActionInfo, session *econtext.SafeSession, skipReset bool) (queryservice.QueryService, error) { if info.alias == nil { return rs.Gateway, nil } @@ -365,18 +369,18 @@ func (stc *ScatterConn) StreamExecuteMulti( query string, rss []*srvtopo.ResolvedShard, bindVars []map[string]*querypb.BindVariable, - session *SafeSession, + session *econtext.SafeSession, autocommit bool, callback func(reply *sqltypes.Result) error, - resultsObserver resultsObserver, + resultsObserver econtext.ResultsObserver, ) []error { - if session.InLockSession() && session.TriggerLockHeartBeat() { + if session.InLockSession() && triggerLockHeartBeat(session) { go stc.runLockQuery(ctx, session) } observedCallback := func(reply *sqltypes.Result) error { if reply != nil { - resultsObserver.observe(reply) + resultsObserver.Observe(reply) } return callback(reply) } @@ -469,10 +473,10 @@ func (stc *ScatterConn) StreamExecuteMulti( default: return nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "[BUG] unexpected actionNeeded on query execution: %v", info.actionNeeded) } - session.logging.log(primitive, rs.Target, rs.Gateway, query, info.actionNeeded == begin || info.actionNeeded == reserveBegin, bindVars[i]) + session.Log(primitive, rs.Target, rs.Gateway, query, info.actionNeeded == begin || info.actionNeeded == reserveBegin, bindVars[i]) - // We need to new shard info irrespective of the error. - newInfo := info.updateTransactionAndReservedID(transactionID, reservedID, alias) + // We need the new shard info irrespective of the error. + newInfo := info.updateTransactionAndReservedID(transactionID, reservedID, alias, nil) if err != nil { return newInfo, err } @@ -604,7 +608,7 @@ func (stc *ScatterConn) multiGo( startTime, statsKey := stc.startAction(name, rs.Target) // Send a dummy session. // TODO(sougou): plumb a real session through this call. - defer stc.endAction(startTime, allErrors, statsKey, &err, NewSafeSession(nil)) + defer stc.endAction(startTime, allErrors, statsKey, &err, econtext.NewSafeSession(nil)) err = action(rs, i) } @@ -646,7 +650,7 @@ func (stc *ScatterConn) multiGoTransaction( ctx context.Context, name string, rss []*srvtopo.ResolvedShard, - session *SafeSession, + session *econtext.SafeSession, autocommit bool, action shardActionTransactionFunc, ) (allErrors *concurrency.AllErrorRecorder) { @@ -662,21 +666,24 @@ func (stc *ScatterConn) multiGoTransaction( startTime, statsKey := stc.startAction(name, rs.Target) defer stc.endAction(startTime, allErrors, statsKey, &err, session) - shardActionInfo, err := actionInfo(ctx, rs.Target, session, autocommit, stc.txConn.mode) + info, shardSession, err := actionInfo(ctx, rs.Target, session, autocommit, stc.txConn.mode) if err != nil { return } - updated, err := action(rs, i, shardActionInfo) - if updated == nil { + info, err = action(rs, i, info) + if info == nil { return } - if updated.actionNeeded != nothing && (updated.transactionID != 0 || updated.reservedID != 0) { - appendErr := session.AppendOrUpdate(&vtgatepb.Session_ShardSession{ - Target: rs.Target, - TransactionId: updated.transactionID, - ReservedId: updated.reservedID, - TabletAlias: updated.alias, - }, stc.txConn.mode) + if info.ignoreOldSession { + shardSession = nil + } + if shardSession != nil && info.rowsAffected { + // We might not always update or append in the session. + // We need to track if rows were affected in the transaction. + shardSession.RowsAffected = info.rowsAffected + } + if info.actionNeeded != nothing && (info.transactionID != 0 || info.reservedID != 0) { + appendErr := session.AppendOrUpdate(rs.Target, info, shardSession, stc.txConn.mode) if appendErr != nil { err = appendErr } @@ -727,7 +734,7 @@ func (stc *ScatterConn) multiGoTransaction( // It returns an error recorder in which each shard error is recorded positionally, // i.e. if rss[2] had an error, then the error recorder will store that error // in the second position. -func (stc *ScatterConn) ExecuteLock(ctx context.Context, rs *srvtopo.ResolvedShard, query *querypb.BoundQuery, session *SafeSession, lockFuncType sqlparser.LockingFuncType) (*sqltypes.Result, error) { +func (stc *ScatterConn) ExecuteLock(ctx context.Context, rs *srvtopo.ResolvedShard, query *querypb.BoundQuery, session *econtext.SafeSession, lockFuncType sqlparser.LockingFuncType) (*sqltypes.Result, error) { var ( qr *sqltypes.Result @@ -830,25 +837,25 @@ func requireNewQS(err error, target *querypb.Target) bool { } // actionInfo looks at the current session, and returns information about what needs to be done for this tablet -func actionInfo(ctx context.Context, target *querypb.Target, session *SafeSession, autocommit bool, txMode vtgatepb.TransactionMode) (*shardActionInfo, error) { +func actionInfo(ctx context.Context, target *querypb.Target, session *econtext.SafeSession, autocommit bool, txMode vtgatepb.TransactionMode) (*shardActionInfo, *vtgatepb.Session_ShardSession, error) { if !(session.InTransaction() || session.InReservedConn()) { - return &shardActionInfo{}, nil + return &shardActionInfo{}, nil, nil } ignoreSession := ctx.Value(engine.IgnoreReserveTxn) if ignoreSession != nil { - return &shardActionInfo{}, nil + return &shardActionInfo{}, nil, nil } // No need to protect ourselves from the race condition between // Find and AppendOrUpdate. The higher level functions ensure that no // duplicate (target) tuples can execute // this at the same time. - transactionID, reservedID, alias, err := session.FindAndChangeSessionIfInSingleTxMode(target.Keyspace, target.Shard, target.TabletType, txMode) + shardSession, err := session.FindAndChangeSessionIfInSingleTxMode(target.Keyspace, target.Shard, target.TabletType, txMode) if err != nil { - return nil, err + return nil, nil, err } - shouldReserve := session.InReservedConn() && reservedID == 0 - shouldBegin := session.InTransaction() && transactionID == 0 && !autocommit + shouldReserve := session.InReservedConn() && (shardSession == nil || shardSession.ReservedId == 0) + shouldBegin := session.InTransaction() && (shardSession == nil || shardSession.TransactionId == 0) && !autocommit var act = nothing switch { @@ -860,16 +867,20 @@ func actionInfo(ctx context.Context, target *querypb.Target, session *SafeSessio act = begin } - return &shardActionInfo{ - actionNeeded: act, - transactionID: transactionID, - reservedID: reservedID, - alias: alias, - }, nil + info := &shardActionInfo{ + actionNeeded: act, + } + if shardSession != nil { + info.transactionID = shardSession.TransactionId + info.reservedID = shardSession.ReservedId + info.alias = shardSession.TabletAlias + info.rowsAffected = shardSession.RowsAffected + } + return info, shardSession, nil } // lockInfo looks at the current session, and returns information about what needs to be done for this tablet -func lockInfo(target *querypb.Target, session *SafeSession, lockFuncType sqlparser.LockingFuncType) (*shardActionInfo, error) { +func lockInfo(target *querypb.Target, session *econtext.SafeSession, lockFuncType sqlparser.LockingFuncType) (*shardActionInfo, error) { info := &shardActionInfo{actionNeeded: nothing} if session.LockSession != nil { if !proto.Equal(target, session.LockSession.Target) { @@ -894,10 +905,35 @@ type shardActionInfo struct { actionNeeded actionNeeded reservedID, transactionID int64 alias *topodatapb.TabletAlias + + // ignoreOldSession is used when there is a retry on the same shard due to connection loss for a reserved connection. + // The old reference should be ignored and new shard session should be added to the session. + ignoreOldSession bool + rowsAffected bool +} + +func (sai *shardActionInfo) TransactionID() int64 { + return sai.transactionID +} + +func (sai *shardActionInfo) ReservedID() int64 { + return sai.reservedID +} + +func (sai *shardActionInfo) RowsAffected() bool { + return sai.rowsAffected +} + +func (sai *shardActionInfo) Alias() *topodatapb.TabletAlias { + return sai.alias } -func (sai *shardActionInfo) updateTransactionAndReservedID(txID int64, rID int64, alias *topodatapb.TabletAlias) *shardActionInfo { - if txID == sai.transactionID && rID == sai.reservedID { +func (sai *shardActionInfo) updateTransactionAndReservedID(txID int64, rID int64, alias *topodatapb.TabletAlias, qr *sqltypes.Result) *shardActionInfo { + firstTimeRowsAffected := false + if txID != 0 && qr != nil && !sai.rowsAffected { + firstTimeRowsAffected = qr.RowsAffected > 0 + } + if txID == sai.transactionID && rID == sai.reservedID && !firstTimeRowsAffected { // As transaction id and reserved id have not changed, there is nothing to update in session shard sessions. return nil } @@ -905,6 +941,7 @@ func (sai *shardActionInfo) updateTransactionAndReservedID(txID int64, rID int64 newInfo.reservedID = rID newInfo.transactionID = txID newInfo.alias = alias + newInfo.rowsAffected = firstTimeRowsAffected return &newInfo } diff --git a/go/vt/vtgate/scatter_conn_test.go b/go/vt/vtgate/scatter_conn_test.go index c5d4f350433..ab8680ca5e6 100644 --- a/go/vt/vtgate/scatter_conn_test.go +++ b/go/vt/vtgate/scatter_conn_test.go @@ -21,6 +21,7 @@ import ( "testing" "vitess.io/vitess/go/vt/log" + econtext "vitess.io/vitess/go/vt/vtgate/executorcontext" "vitess.io/vitess/go/mysql/sqlerror" vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" @@ -100,7 +101,7 @@ func TestExecuteFailOnAutocommit(t *testing.T) { }, Autocommit: false, } - _, errs := sc.ExecuteMultiShard(ctx, nil, rss, queries, NewSafeSession(session), true /*autocommit*/, false, nullResultsObserver{}) + _, errs := sc.ExecuteMultiShard(ctx, nil, rss, queries, econtext.NewSafeSession(session), true /*autocommit*/, false, nullResultsObserver{}) err := vterrors.Aggregate(errs) require.Error(t, err) require.Contains(t, err.Error(), "in autocommit mode, transactionID should be zero but was: 123") @@ -183,7 +184,7 @@ func TestExecutePanic(t *testing.T) { require.Contains(t, logMessage, "(*ScatterConn).multiGoTransaction") }() - _, _ = sc.ExecuteMultiShard(ctx, nil, rss, queries, NewSafeSession(session), true /*autocommit*/, false, nullResultsObserver{}) + _, _ = sc.ExecuteMultiShard(ctx, nil, rss, queries, econtext.NewSafeSession(session), true /*autocommit*/, false, nullResultsObserver{}) } @@ -204,7 +205,7 @@ func TestReservedOnMultiReplica(t *testing.T) { res := srvtopo.NewResolver(newSandboxForCells(ctx, []string{"aa"}), sc.gateway, "aa") - session := NewSafeSession(&vtgatepb.Session{InTransaction: false, InReservedConn: true}) + session := econtext.NewSafeSession(&vtgatepb.Session{InTransaction: false, InReservedConn: true}) destinations := []key.Destination{key.DestinationShard("0")} for i := 0; i < 10; i++ { executeOnShards(t, ctx, res, keyspace, sc, session, destinations) @@ -351,7 +352,7 @@ func TestReservedBeginTableDriven(t *testing.T) { res := srvtopo.NewResolver(newSandboxForCells(ctx, []string{"aa"}), sc.gateway, "aa") t.Run(test.name, func(t *testing.T) { - session := NewSafeSession(&vtgatepb.Session{}) + session := econtext.NewSafeSession(&vtgatepb.Session{}) for _, action := range test.actions { session.Session.InTransaction = action.transaction session.Session.InReservedConn = action.reserved @@ -384,7 +385,7 @@ func TestReservedConnFail(t *testing.T) { _ = hc.AddTestTablet("aa", "1", 1, keyspace, "1", topodatapb.TabletType_REPLICA, true, 1, nil) res := srvtopo.NewResolver(newSandboxForCells(ctx, []string{"aa"}), sc.gateway, "aa") - session := NewSafeSession(&vtgatepb.Session{InTransaction: false, InReservedConn: true}) + session := econtext.NewSafeSession(&vtgatepb.Session{InTransaction: false, InReservedConn: true}) destinations := []key.Destination{key.DestinationShard("0")} executeOnShards(t, ctx, res, keyspace, sc, session, destinations) diff --git a/go/vt/vtgate/tabletgateway_flaky_test.go b/go/vt/vtgate/tabletgateway_flaky_test.go index d136542d176..124997bea9e 100644 --- a/go/vt/vtgate/tabletgateway_flaky_test.go +++ b/go/vt/vtgate/tabletgateway_flaky_test.go @@ -20,6 +20,8 @@ import ( "testing" "time" + econtext "vitess.io/vitess/go/vt/vtgate/executorcontext" + "github.com/stretchr/testify/require" "vitess.io/vitess/go/mysql/collations" @@ -53,7 +55,7 @@ func TestGatewayBufferingWhenPrimarySwitchesServingState(t *testing.T) { TabletType: tabletType, } - ts := &fakeTopoServer{} + ts := &econtext.FakeTopoServer{} // create a new fake health check. We want to check the buffering code which uses Subscribe, so we must also pass a channel hc := discovery.NewFakeHealthCheck(make(chan *discovery.TabletHealth)) // create a new tablet gateway @@ -156,7 +158,7 @@ func TestGatewayBufferingWhileReparenting(t *testing.T) { TabletType: tabletType, } - ts := &fakeTopoServer{} + ts := &econtext.FakeTopoServer{} // create a new fake health check. We want to check the buffering code which uses Subscribe, so we must also pass a channel hc := discovery.NewFakeHealthCheck(make(chan *discovery.TabletHealth)) // create a new tablet gateway @@ -286,7 +288,7 @@ func TestInconsistentStateDetectedBuffering(t *testing.T) { TabletType: tabletType, } - ts := &fakeTopoServer{} + ts := &econtext.FakeTopoServer{} // create a new fake health check. We want to check the buffering code which uses Subscribe, so we must also pass a channel hc := discovery.NewFakeHealthCheck(make(chan *discovery.TabletHealth)) // create a new tablet gateway diff --git a/go/vt/vtgate/tabletgateway_test.go b/go/vt/vtgate/tabletgateway_test.go index 2aafb78af99..b318cb84981 100644 --- a/go/vt/vtgate/tabletgateway_test.go +++ b/go/vt/vtgate/tabletgateway_test.go @@ -22,6 +22,8 @@ import ( "strings" "testing" + econtext "vitess.io/vitess/go/vt/vtgate/executorcontext" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -109,7 +111,7 @@ func TestTabletGatewayShuffleTablets(t *testing.T) { ctx := utils.LeakCheckContext(t) hc := discovery.NewFakeHealthCheck(nil) - ts := &fakeTopoServer{} + ts := &econtext.FakeTopoServer{} tg := NewTabletGateway(ctx, hc, ts, "local") defer tg.Close(ctx) @@ -183,7 +185,7 @@ func TestTabletGatewayReplicaTransactionError(t *testing.T) { TabletType: tabletType, } hc := discovery.NewFakeHealthCheck(nil) - ts := &fakeTopoServer{} + ts := &econtext.FakeTopoServer{} tg := NewTabletGateway(ctx, hc, ts, "cell") defer tg.Close(ctx) @@ -218,7 +220,7 @@ func testTabletGatewayGenericHelper(t *testing.T, ctx context.Context, f func(ct TabletType: tabletType, } hc := discovery.NewFakeHealthCheck(nil) - ts := &fakeTopoServer{} + ts := &econtext.FakeTopoServer{} tg := NewTabletGateway(ctx, hc, ts, "cell") defer tg.Close(ctx) // no tablet @@ -306,7 +308,7 @@ func testTabletGatewayTransact(t *testing.T, ctx context.Context, f func(ctx con TabletType: tabletType, } hc := discovery.NewFakeHealthCheck(nil) - ts := &fakeTopoServer{} + ts := &econtext.FakeTopoServer{} tg := NewTabletGateway(ctx, hc, ts, "cell") defer tg.Close(ctx) @@ -348,7 +350,7 @@ func verifyShardErrors(t *testing.T, err error, wantErrors []string, wantCode vt // TestWithRetry tests the functionality of withRetry function in different circumstances. func TestWithRetry(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) - tg := NewTabletGateway(ctx, discovery.NewFakeHealthCheck(nil), &fakeTopoServer{}, "cell") + tg := NewTabletGateway(ctx, discovery.NewFakeHealthCheck(nil), &econtext.FakeTopoServer{}, "cell") tg.kev = discovery.NewKeyspaceEventWatcher(ctx, tg.srvTopoServer, tg.hc, tg.localCell) defer func() { cancel() diff --git a/go/vt/vtgate/tx_conn.go b/go/vt/vtgate/tx_conn.go index 315484ea499..3ce138bc0e4 100644 --- a/go/vt/vtgate/tx_conn.go +++ b/go/vt/vtgate/tx_conn.go @@ -33,6 +33,7 @@ import ( vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vterrors" + econtext "vitess.io/vitess/go/vt/vtgate/executorcontext" "vitess.io/vitess/go/vt/vttablet/queryservice" ) @@ -80,7 +81,7 @@ var phaseMessage = map[commitPhase]string{ // Begin begins a new transaction. If one is already in progress, it commits it // and starts a new one. -func (txc *TxConn) Begin(ctx context.Context, session *SafeSession, txAccessModes []sqlparser.TxAccessMode) error { +func (txc *TxConn) Begin(ctx context.Context, session *econtext.SafeSession, txAccessModes []sqlparser.TxAccessMode) error { if session.InTransaction() { if err := txc.Commit(ctx, session); err != nil { return err @@ -102,7 +103,7 @@ func (txc *TxConn) Begin(ctx context.Context, session *SafeSession, txAccessMode // Commit commits the current transaction. The type of commit can be // best effort or 2pc depending on the session setting. -func (txc *TxConn) Commit(ctx context.Context, session *SafeSession) error { +func (txc *TxConn) Commit(ctx context.Context, session *econtext.SafeSession) error { defer session.ResetTx() if !session.InTransaction() { return nil @@ -123,7 +124,7 @@ func (txc *TxConn) Commit(ctx context.Context, session *SafeSession) error { return txc.commitNormal(ctx, session) } -func recordCommitTime(session *SafeSession, twopc bool, startTime time.Time) { +func recordCommitTime(session *econtext.SafeSession, twopc bool, startTime time.Time) { switch { case len(session.ShardSessions) == 0: // No-op @@ -143,7 +144,7 @@ func (txc *TxConn) queryService(ctx context.Context, alias *topodatapb.TabletAli return txc.tabletGateway.QueryServiceByAlias(ctx, alias, nil) } -func (txc *TxConn) commitShard(ctx context.Context, s *vtgatepb.Session_ShardSession, logging *executeLogger) error { +func (txc *TxConn) commitShard(ctx context.Context, s *vtgatepb.Session_ShardSession, logging *econtext.ExecuteLogger) error { if s.TransactionId == 0 { return nil } @@ -159,19 +160,19 @@ func (txc *TxConn) commitShard(ctx context.Context, s *vtgatepb.Session_ShardSes } s.TransactionId = 0 s.ReservedId = reservedID - logging.log(nil, s.Target, nil, "commit", false, nil) + logging.Log(nil, s.Target, nil, "commit", false, nil) return nil } -func (txc *TxConn) commitNormal(ctx context.Context, session *SafeSession) error { - if err := txc.runSessions(ctx, session.PreSessions, session.logging, txc.commitShard); err != nil { +func (txc *TxConn) commitNormal(ctx context.Context, session *econtext.SafeSession) error { + if err := txc.runSessions(ctx, session.PreSessions, session.GetLogger(), txc.commitShard); err != nil { _ = txc.Release(ctx, session) return err } // Retain backward compatibility on commit order for the normal session. for i, shardSession := range session.ShardSessions { - if err := txc.commitShard(ctx, shardSession, session.logging); err != nil { + if err := txc.commitShard(ctx, shardSession, session.GetLogger()); err != nil { if i > 0 { nShards := i elipsis := false @@ -197,7 +198,7 @@ func (txc *TxConn) commitNormal(ctx context.Context, session *SafeSession) error } } - if err := txc.runSessions(ctx, session.PostSessions, session.logging, txc.commitShard); err != nil { + if err := txc.runSessions(ctx, session.PostSessions, session.GetLogger(), txc.commitShard); err != nil { // If last commit fails, there will be nothing to rollback. session.RecordWarning(&querypb.QueryWarning{Message: fmt.Sprintf("post-operation transaction had an error: %v", err)}) // With reserved connection we should release them. @@ -209,7 +210,7 @@ func (txc *TxConn) commitNormal(ctx context.Context, session *SafeSession) error } // commit2PC will not used the pinned tablets - to make sure we use the current source, we need to use the gateway's queryservice -func (txc *TxConn) commit2PC(ctx context.Context, session *SafeSession) (err error) { +func (txc *TxConn) commit2PC(ctx context.Context, session *econtext.SafeSession) (err error) { // If the number of participants is one or less, then it's a normal commit. if len(session.ShardSessions) <= 1 { return txc.commitNormal(ctx, session) @@ -249,7 +250,7 @@ func (txc *TxConn) commit2PC(ctx context.Context, session *SafeSession) (err err } txPhase = Commit2pcPrepare - prepareAction := func(ctx context.Context, s *vtgatepb.Session_ShardSession, logging *executeLogger) error { + prepareAction := func(ctx context.Context, s *vtgatepb.Session_ShardSession, logging *econtext.ExecuteLogger) error { if DebugTwoPc { // Test code to simulate a failure during RM prepare if terr := checkTestFailure(ctx, "RMPrepare_-40_FailNow", s.Target); terr != nil { return terr @@ -257,7 +258,7 @@ func (txc *TxConn) commit2PC(ctx context.Context, session *SafeSession) (err err } return txc.tabletGateway.Prepare(ctx, s.Target, s.TransactionId, dtid) } - if err = txc.runSessions(ctx, rmShards, session.logging, prepareAction); err != nil { + if err = txc.runSessions(ctx, rmShards, session.GetLogger(), prepareAction); err != nil { return err } @@ -280,7 +281,7 @@ func (txc *TxConn) commit2PC(ctx context.Context, session *SafeSession) (err err } txPhase = Commit2pcPrepareCommit - prepareCommitAction := func(ctx context.Context, s *vtgatepb.Session_ShardSession, logging *executeLogger) error { + prepareCommitAction := func(ctx context.Context, s *vtgatepb.Session_ShardSession, logging *econtext.ExecuteLogger) error { if DebugTwoPc { // Test code to simulate a failure during RM prepare if terr := checkTestFailure(ctx, "RMCommit_-40_FailNow", s.Target); terr != nil { return terr @@ -288,7 +289,7 @@ func (txc *TxConn) commit2PC(ctx context.Context, session *SafeSession) (err err } return txc.tabletGateway.CommitPrepared(ctx, s.Target, dtid) } - if err = txc.runSessions(ctx, rmShards, session.logging, prepareCommitAction); err != nil { + if err = txc.runSessions(ctx, rmShards, session.GetLogger(), prepareCommitAction); err != nil { return err } @@ -300,7 +301,7 @@ func (txc *TxConn) commit2PC(ctx context.Context, session *SafeSession) (err err return nil } -func (txc *TxConn) checkValidCondition(session *SafeSession) error { +func (txc *TxConn) checkValidCondition(session *econtext.SafeSession) error { if len(session.PreSessions) != 0 || len(session.PostSessions) != 0 { return vterrors.VT12001("atomic distributed transaction commit with consistent lookup vindex") } @@ -309,7 +310,7 @@ func (txc *TxConn) checkValidCondition(session *SafeSession) error { func (txc *TxConn) errActionAndLogWarn( ctx context.Context, - session *SafeSession, + session *econtext.SafeSession, txPhase commitPhase, startCommitState querypb.StartCommitState, dtid string, @@ -323,12 +324,12 @@ func (txc *TxConn) errActionAndLogWarn( rollbackErr = txc.Rollback(ctx, session) case Commit2pcPrepare: // Rollback the prepared and unprepared transactions. - rollbackErr = txc.rollbackTx(ctx, dtid, mmShard, rmShards, session.logging) + rollbackErr = txc.rollbackTx(ctx, dtid, mmShard, rmShards, session.GetLogger()) case Commit2pcStartCommit: // Failed to store the commit decision on MM. // If the failure state is certain, then the only option is to rollback the prepared transactions on the RMs. if startCommitState == querypb.StartCommitState_Fail { - rollbackErr = txc.rollbackTx(ctx, dtid, mmShard, rmShards, session.logging) + rollbackErr = txc.rollbackTx(ctx, dtid, mmShard, rmShards, session.GetLogger()) } fallthrough case Commit2pcPrepareCommit: @@ -362,7 +363,7 @@ func createWarningMessage(dtid string, txPhase commitPhase) string { } // Rollback rolls back the current transaction. There are no retries on this operation. -func (txc *TxConn) Rollback(ctx context.Context, session *SafeSession) error { +func (txc *TxConn) Rollback(ctx context.Context, session *econtext.SafeSession) error { if !session.InTransaction() { return nil } @@ -371,7 +372,7 @@ func (txc *TxConn) Rollback(ctx context.Context, session *SafeSession) error { allsessions := append(session.PreSessions, session.ShardSessions...) allsessions = append(allsessions, session.PostSessions...) - err := txc.runSessions(ctx, allsessions, session.logging, func(ctx context.Context, s *vtgatepb.Session_ShardSession, logging *executeLogger) error { + err := txc.runSessions(ctx, allsessions, session.GetLogger(), func(ctx context.Context, s *vtgatepb.Session_ShardSession, logging *econtext.ExecuteLogger) error { if s.TransactionId == 0 { return nil } @@ -385,7 +386,7 @@ func (txc *TxConn) Rollback(ctx context.Context, session *SafeSession) error { } s.TransactionId = 0 s.ReservedId = reservedID - logging.log(nil, s.Target, nil, "rollback", false, nil) + logging.Log(nil, s.Target, nil, "rollback", false, nil) return nil }) if err != nil { @@ -398,7 +399,7 @@ func (txc *TxConn) Rollback(ctx context.Context, session *SafeSession) error { } // Release releases the reserved connection and/or rollbacks the transaction -func (txc *TxConn) Release(ctx context.Context, session *SafeSession) error { +func (txc *TxConn) Release(ctx context.Context, session *econtext.SafeSession) error { if !session.InTransaction() && !session.InReservedConn() { return nil } @@ -407,7 +408,7 @@ func (txc *TxConn) Release(ctx context.Context, session *SafeSession) error { allsessions := append(session.PreSessions, session.ShardSessions...) allsessions = append(allsessions, session.PostSessions...) - return txc.runSessions(ctx, allsessions, session.logging, func(ctx context.Context, s *vtgatepb.Session_ShardSession, logging *executeLogger) error { + return txc.runSessions(ctx, allsessions, session.GetLogger(), func(ctx context.Context, s *vtgatepb.Session_ShardSession, logging *econtext.ExecuteLogger) error { if s.ReservedId == 0 && s.TransactionId == 0 { return nil } @@ -426,7 +427,7 @@ func (txc *TxConn) Release(ctx context.Context, session *SafeSession) error { } // ReleaseLock releases the reserved connection used for locking. -func (txc *TxConn) ReleaseLock(ctx context.Context, session *SafeSession) error { +func (txc *TxConn) ReleaseLock(ctx context.Context, session *econtext.SafeSession) error { if !session.InLockSession() { return nil } @@ -445,7 +446,7 @@ func (txc *TxConn) ReleaseLock(ctx context.Context, session *SafeSession) error } // ReleaseAll releases all the shard sessions and lock session. -func (txc *TxConn) ReleaseAll(ctx context.Context, session *SafeSession) error { +func (txc *TxConn) ReleaseAll(ctx context.Context, session *econtext.SafeSession) error { if !session.InTransaction() && !session.InReservedConn() && !session.InLockSession() { return nil } @@ -457,7 +458,7 @@ func (txc *TxConn) ReleaseAll(ctx context.Context, session *SafeSession) error { allsessions = append(allsessions, session.LockSession) } - return txc.runSessions(ctx, allsessions, session.logging, func(ctx context.Context, s *vtgatepb.Session_ShardSession, loggging *executeLogger) error { + return txc.runSessions(ctx, allsessions, session.GetLogger(), func(ctx context.Context, s *vtgatepb.Session_ShardSession, loggging *econtext.ExecuteLogger) error { if s.ReservedId == 0 && s.TransactionId == 0 { return nil } @@ -529,12 +530,12 @@ func (txc *TxConn) resolveTx(ctx context.Context, target *querypb.Target, transa // rollbackTx rollbacks the specified distributed transaction. // Rollbacks happens on the metadata manager and all participants irrespective of the failure. -func (txc *TxConn) rollbackTx(ctx context.Context, dtid string, mmShard *vtgatepb.Session_ShardSession, participants []*vtgatepb.Session_ShardSession, logging *executeLogger) error { +func (txc *TxConn) rollbackTx(ctx context.Context, dtid string, mmShard *vtgatepb.Session_ShardSession, participants []*vtgatepb.Session_ShardSession, logging *econtext.ExecuteLogger) error { var errs []error if mmErr := txc.rollbackMM(ctx, dtid, mmShard); mmErr != nil { errs = append(errs, mmErr) } - if rmErr := txc.runSessions(ctx, participants, logging, func(ctx context.Context, session *vtgatepb.Session_ShardSession, logger *executeLogger) error { + if rmErr := txc.runSessions(ctx, participants, logging, func(ctx context.Context, session *vtgatepb.Session_ShardSession, logger *econtext.ExecuteLogger) error { return txc.tabletGateway.RollbackPrepared(ctx, session.Target, dtid, session.TransactionId) }); rmErr != nil { errs = append(errs, rmErr) @@ -575,7 +576,7 @@ func (txc *TxConn) resumeCommit(ctx context.Context, target *querypb.Target, tra } // runSessions executes the action for all shardSessions in parallel and returns a consolidated error. -func (txc *TxConn) runSessions(ctx context.Context, shardSessions []*vtgatepb.Session_ShardSession, logging *executeLogger, action func(context.Context, *vtgatepb.Session_ShardSession, *executeLogger) error) error { +func (txc *TxConn) runSessions(ctx context.Context, shardSessions []*vtgatepb.Session_ShardSession, logging *econtext.ExecuteLogger, action func(context.Context, *vtgatepb.Session_ShardSession, *econtext.ExecuteLogger) error) error { // Fastpath. if len(shardSessions) == 1 { return action(ctx, shardSessions[0], logging) diff --git a/go/vt/vtgate/tx_conn_test.go b/go/vt/vtgate/tx_conn_test.go index 9d49626f6f1..333094569c8 100644 --- a/go/vt/vtgate/tx_conn_test.go +++ b/go/vt/vtgate/tx_conn_test.go @@ -26,6 +26,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + econtext "vitess.io/vitess/go/vt/vtgate/executorcontext" + "vitess.io/vitess/go/event/syslogger" "vitess.io/vitess/go/mysql/sqlerror" "vitess.io/vitess/go/test/utils" @@ -51,7 +53,7 @@ func TestTxConnBegin(t *testing.T) { session := &vtgatepb.Session{} // begin - safeSession := NewSafeSession(session) + safeSession := econtext.NewSafeSession(session) err := sc.txConn.Begin(ctx, safeSession, nil) require.NoError(t, err) wantSession := vtgatepb.Session{InTransaction: true} @@ -75,7 +77,7 @@ func TestTxConnCommitFailure(t *testing.T) { // Sequence the executes to ensure commit order - session := NewSafeSession(&vtgatepb.Session{InTransaction: true}) + session := econtext.NewSafeSession(&vtgatepb.Session{InTransaction: true}) sc.ExecuteMultiShard(ctx, nil, rssm[0], queries, session, false, false, nullResultsObserver{}) wantSession := vtgatepb.Session{ InTransaction: true, @@ -176,7 +178,7 @@ func TestTxConnCommitFailureAfterNonAtomicCommitMaxShards(t *testing.T) { // Sequence the executes to ensure commit order - session := NewSafeSession(&vtgatepb.Session{InTransaction: true}) + session := econtext.NewSafeSession(&vtgatepb.Session{InTransaction: true}) wantSession := vtgatepb.Session{ InTransaction: true, ShardSessions: []*vtgatepb.Session_ShardSession{}, @@ -230,7 +232,7 @@ func TestTxConnCommitFailureBeforeNonAtomicCommitMaxShards(t *testing.T) { // Sequence the executes to ensure commit order - session := NewSafeSession(&vtgatepb.Session{InTransaction: true}) + session := econtext.NewSafeSession(&vtgatepb.Session{InTransaction: true}) wantSession := vtgatepb.Session{ InTransaction: true, ShardSessions: []*vtgatepb.Session_ShardSession{}, @@ -282,7 +284,7 @@ func TestTxConnCommitSuccess(t *testing.T) { sc.txConn.mode = vtgatepb.TransactionMode_MULTI // Sequence the executes to ensure commit order - session := NewSafeSession(&vtgatepb.Session{InTransaction: true}) + session := econtext.NewSafeSession(&vtgatepb.Session{InTransaction: true}) sc.ExecuteMultiShard(ctx, nil, rss0, queries, session, false, false, nullResultsObserver{}) wantSession := vtgatepb.Session{ InTransaction: true, @@ -335,7 +337,7 @@ func TestTxConnReservedCommitSuccess(t *testing.T) { sc.txConn.mode = vtgatepb.TransactionMode_MULTI // Sequence the executes to ensure commit order - session := NewSafeSession(&vtgatepb.Session{InTransaction: true, InReservedConn: true}) + session := econtext.NewSafeSession(&vtgatepb.Session{InTransaction: true, InReservedConn: true}) sc.ExecuteMultiShard(ctx, nil, rss0, queries, session, false, false, nullResultsObserver{}) wantSession := vtgatepb.Session{ InTransaction: true, @@ -420,7 +422,7 @@ func TestTxConnReservedOn2ShardTxOn1ShardAndCommit(t *testing.T) { sc.txConn.mode = vtgatepb.TransactionMode_MULTI // Sequence the executes to ensure shard session order - session := NewSafeSession(&vtgatepb.Session{InReservedConn: true}) + session := econtext.NewSafeSession(&vtgatepb.Session{InReservedConn: true}) // this will create reserved connections against all tablets _, errs := sc.ExecuteMultiShard(ctx, nil, rss1, queries, session, false, false, nullResultsObserver{}) @@ -515,7 +517,7 @@ func TestTxConnReservedOn2ShardTxOn1ShardAndRollback(t *testing.T) { sc.txConn.mode = vtgatepb.TransactionMode_MULTI // Sequence the executes to ensure shard session order - session := NewSafeSession(&vtgatepb.Session{InReservedConn: true}) + session := econtext.NewSafeSession(&vtgatepb.Session{InReservedConn: true}) // this will create reserved connections against all tablets _, errs := sc.ExecuteMultiShard(ctx, nil, rss1, queries, session, false, false, nullResultsObserver{}) @@ -611,7 +613,7 @@ func TestTxConnCommitOrderFailure1(t *testing.T) { queries := []*querypb.BoundQuery{{Sql: "query1"}} // Sequence the executes to ensure commit order - session := NewSafeSession(&vtgatepb.Session{InTransaction: true}) + session := econtext.NewSafeSession(&vtgatepb.Session{InTransaction: true}) sc.ExecuteMultiShard(ctx, nil, rss0, queries, session, false, false, nullResultsObserver{}) session.SetCommitOrder(vtgatepb.CommitOrder_PRE) @@ -646,7 +648,7 @@ func TestTxConnCommitOrderFailure2(t *testing.T) { }} // Sequence the executes to ensure commit order - session := NewSafeSession(&vtgatepb.Session{InTransaction: true}) + session := econtext.NewSafeSession(&vtgatepb.Session{InTransaction: true}) sc.ExecuteMultiShard(context.Background(), nil, rss1, queries, session, false, false, nullResultsObserver{}) session.SetCommitOrder(vtgatepb.CommitOrder_PRE) @@ -680,7 +682,7 @@ func TestTxConnCommitOrderFailure3(t *testing.T) { }} // Sequence the executes to ensure commit order - session := NewSafeSession(&vtgatepb.Session{InTransaction: true}) + session := econtext.NewSafeSession(&vtgatepb.Session{InTransaction: true}) sc.ExecuteMultiShard(ctx, nil, rss0, queries, session, false, false, nullResultsObserver{}) session.SetCommitOrder(vtgatepb.CommitOrder_PRE) @@ -722,7 +724,7 @@ func TestTxConnCommitOrderSuccess(t *testing.T) { }} // Sequence the executes to ensure commit order - session := NewSafeSession(&vtgatepb.Session{InTransaction: true}) + session := econtext.NewSafeSession(&vtgatepb.Session{InTransaction: true}) sc.ExecuteMultiShard(ctx, nil, rss0, queries, session, false, false, nullResultsObserver{}) wantSession := vtgatepb.Session{ InTransaction: true, @@ -820,7 +822,7 @@ func TestTxConnReservedCommitOrderSuccess(t *testing.T) { }} // Sequence the executes to ensure commit order - session := NewSafeSession(&vtgatepb.Session{InTransaction: true, InReservedConn: true}) + session := econtext.NewSafeSession(&vtgatepb.Session{InTransaction: true, InReservedConn: true}) sc.ExecuteMultiShard(ctx, nil, rss0, queries, session, false, false, nullResultsObserver{}) wantSession := vtgatepb.Session{ InTransaction: true, @@ -957,7 +959,7 @@ func TestTxConnCommit2PC(t *testing.T) { sc, sbc0, sbc1, rss0, _, rss01 := newTestTxConnEnv(t, ctx, "TestTxConnCommit2PC") - session := NewSafeSession(&vtgatepb.Session{InTransaction: true}) + session := econtext.NewSafeSession(&vtgatepb.Session{InTransaction: true}) sc.ExecuteMultiShard(ctx, nil, rss0, queries, session, false, false, nullResultsObserver{}) sc.ExecuteMultiShard(ctx, nil, rss01, twoQueries, session, false, false, nullResultsObserver{}) session.TransactionMode = vtgatepb.TransactionMode_TWOPC @@ -974,7 +976,7 @@ func TestTxConnCommit2PCOneParticipant(t *testing.T) { ctx := utils.LeakCheckContext(t) sc, sbc0, _, rss0, _, _ := newTestTxConnEnv(t, ctx, "TestTxConnCommit2PCOneParticipant") - session := NewSafeSession(&vtgatepb.Session{InTransaction: true}) + session := econtext.NewSafeSession(&vtgatepb.Session{InTransaction: true}) sc.ExecuteMultiShard(ctx, nil, rss0, queries, session, false, false, nullResultsObserver{}) session.TransactionMode = vtgatepb.TransactionMode_TWOPC require.NoError(t, @@ -987,7 +989,7 @@ func TestTxConnCommit2PCCreateTransactionFail(t *testing.T) { sc, sbc0, sbc1, rss0, rss1, _ := newTestTxConnEnv(t, ctx, "TestTxConnCommit2PCCreateTransactionFail") - session := NewSafeSession(&vtgatepb.Session{InTransaction: true}) + session := econtext.NewSafeSession(&vtgatepb.Session{InTransaction: true}) sc.ExecuteMultiShard(ctx, nil, rss0, queries, session, false, false, nullResultsObserver{}) sc.ExecuteMultiShard(ctx, nil, rss1, queries, session, false, false, nullResultsObserver{}) @@ -1009,7 +1011,7 @@ func TestTxConnCommit2PCPrepareFail(t *testing.T) { sc, sbc0, sbc1, rss0, _, rss01 := newTestTxConnEnv(t, ctx, "TestTxConnCommit2PCPrepareFail") - session := NewSafeSession(&vtgatepb.Session{InTransaction: true}) + session := econtext.NewSafeSession(&vtgatepb.Session{InTransaction: true}) sc.ExecuteMultiShard(ctx, nil, rss0, queries, session, false, false, nullResultsObserver{}) sc.ExecuteMultiShard(ctx, nil, rss01, twoQueries, session, false, false, nullResultsObserver{}) @@ -1035,7 +1037,7 @@ func TestTxConnCommit2PCStartCommitFail(t *testing.T) { sc, sbc0, sbc1, rss0, _, rss01 := newTestTxConnEnv(t, ctx, "TestTxConnCommit2PCStartCommitFail") - session := NewSafeSession(&vtgatepb.Session{InTransaction: true}) + session := econtext.NewSafeSession(&vtgatepb.Session{InTransaction: true}) sc.ExecuteMultiShard(ctx, nil, rss0, queries, session, false, false, nullResultsObserver{}) sc.ExecuteMultiShard(ctx, nil, rss01, twoQueries, session, false, false, nullResultsObserver{}) @@ -1054,7 +1056,7 @@ func TestTxConnCommit2PCStartCommitFail(t *testing.T) { sbc0.ResetCounter() sbc1.ResetCounter() - session = NewSafeSession(&vtgatepb.Session{InTransaction: true}) + session = econtext.NewSafeSession(&vtgatepb.Session{InTransaction: true}) sc.ExecuteMultiShard(ctx, nil, rss0, queries, session, false, false, nullResultsObserver{}) sc.ExecuteMultiShard(ctx, nil, rss01, twoQueries, session, false, false, nullResultsObserver{}) @@ -1077,7 +1079,7 @@ func TestTxConnCommit2PCCommitPreparedFail(t *testing.T) { sc, sbc0, sbc1, rss0, _, rss01 := newTestTxConnEnv(t, ctx, "TestTxConnCommit2PCCommitPreparedFail") - session := NewSafeSession(&vtgatepb.Session{InTransaction: true}) + session := econtext.NewSafeSession(&vtgatepb.Session{InTransaction: true}) sc.ExecuteMultiShard(ctx, nil, rss0, queries, session, false, false, nullResultsObserver{}) sc.ExecuteMultiShard(ctx, nil, rss01, twoQueries, session, false, false, nullResultsObserver{}) @@ -1097,7 +1099,7 @@ func TestTxConnCommit2PCConcludeTransactionFail(t *testing.T) { sc, sbc0, sbc1, rss0, _, rss01 := newTestTxConnEnv(t, ctx, "TestTxConnCommit2PCConcludeTransactionFail") - session := NewSafeSession(&vtgatepb.Session{InTransaction: true}) + session := econtext.NewSafeSession(&vtgatepb.Session{InTransaction: true}) sc.ExecuteMultiShard(ctx, nil, rss0, queries, session, false, false, nullResultsObserver{}) sc.ExecuteMultiShard(ctx, nil, rss01, twoQueries, session, false, false, nullResultsObserver{}) @@ -1117,7 +1119,7 @@ func TestTxConnRollback(t *testing.T) { sc, sbc0, sbc1, rss0, _, rss01 := newTestTxConnEnv(t, ctx, "TxConnRollback") - session := NewSafeSession(&vtgatepb.Session{InTransaction: true}) + session := econtext.NewSafeSession(&vtgatepb.Session{InTransaction: true}) sc.ExecuteMultiShard(ctx, nil, rss0, queries, session, false, false, nullResultsObserver{}) sc.ExecuteMultiShard(ctx, nil, rss01, twoQueries, session, false, false, nullResultsObserver{}) require.NoError(t, @@ -1133,7 +1135,7 @@ func TestTxConnReservedRollback(t *testing.T) { sc, sbc0, sbc1, rss0, _, rss01 := newTestTxConnEnv(t, ctx, "TxConnReservedRollback") - session := NewSafeSession(&vtgatepb.Session{InTransaction: true, InReservedConn: true}) + session := econtext.NewSafeSession(&vtgatepb.Session{InTransaction: true, InReservedConn: true}) sc.ExecuteMultiShard(ctx, nil, rss0, queries, session, false, false, nullResultsObserver{}) sc.ExecuteMultiShard(ctx, nil, rss01, twoQueries, session, false, false, nullResultsObserver{}) require.NoError(t, @@ -1170,7 +1172,7 @@ func TestTxConnReservedRollbackFailure(t *testing.T) { sc, sbc0, sbc1, rss0, rss1, rss01 := newTestTxConnEnv(t, ctx, "TxConnReservedRollback") - session := NewSafeSession(&vtgatepb.Session{InTransaction: true, InReservedConn: true}) + session := econtext.NewSafeSession(&vtgatepb.Session{InTransaction: true, InReservedConn: true}) sc.ExecuteMultiShard(ctx, nil, rss0, queries, session, false, false, nullResultsObserver{}) sc.ExecuteMultiShard(ctx, nil, rss01, twoQueries, session, false, false, nullResultsObserver{}) @@ -1449,7 +1451,7 @@ func TestTxConnMultiGoSessions(t *testing.T) { Keyspace: "0", }, }} - err := txc.runSessions(ctx, input, nil, func(ctx context.Context, s *vtgatepb.Session_ShardSession, logger *executeLogger) error { + err := txc.runSessions(ctx, input, nil, func(ctx context.Context, s *vtgatepb.Session_ShardSession, logger *econtext.ExecuteLogger) error { return vterrors.Errorf(vtrpcpb.Code_INTERNAL, "err %s", s.Target.Keyspace) }) want := "err 0" @@ -1464,7 +1466,7 @@ func TestTxConnMultiGoSessions(t *testing.T) { Keyspace: "1", }, }} - err = txc.runSessions(ctx, input, nil, func(ctx context.Context, s *vtgatepb.Session_ShardSession, logger *executeLogger) error { + err = txc.runSessions(ctx, input, nil, func(ctx context.Context, s *vtgatepb.Session_ShardSession, logger *econtext.ExecuteLogger) error { return vterrors.Errorf(vtrpcpb.Code_INTERNAL, "err %s", s.Target.Keyspace) }) want = "err 0\nerr 1" @@ -1472,7 +1474,7 @@ func TestTxConnMultiGoSessions(t *testing.T) { wantCode := vtrpcpb.Code_INTERNAL assert.Equal(t, wantCode, vterrors.Code(err), "error code") - err = txc.runSessions(ctx, input, nil, func(ctx context.Context, s *vtgatepb.Session_ShardSession, logger *executeLogger) error { + err = txc.runSessions(ctx, input, nil, func(ctx context.Context, s *vtgatepb.Session_ShardSession, logger *econtext.ExecuteLogger) error { return nil }) require.NoError(t, err) @@ -1515,7 +1517,7 @@ func TestTxConnAccessModeReset(t *testing.T) { tcases := []struct { name string - f func(ctx context.Context, session *SafeSession) error + f func(ctx context.Context, session *econtext.SafeSession) error }{{ name: "begin-commit", f: sc.txConn.Commit, @@ -1532,7 +1534,7 @@ func TestTxConnAccessModeReset(t *testing.T) { for _, tcase := range tcases { t.Run(tcase.name, func(t *testing.T) { - safeSession := NewSafeSession(&vtgatepb.Session{ + safeSession := econtext.NewSafeSession(&vtgatepb.Session{ Options: &querypb.ExecuteOptions{ TransactionAccessMode: []querypb.ExecuteOptions_TransactionAccessMode{querypb.ExecuteOptions_READ_ONLY}, }, diff --git a/go/vt/vtgate/viperconfig.go b/go/vt/vtgate/viperconfig.go new file mode 100644 index 00000000000..ec77ff62d4f --- /dev/null +++ b/go/vt/vtgate/viperconfig.go @@ -0,0 +1,16 @@ +package vtgate + +import "vitess.io/vitess/go/viperutil" + +type dynamicViperConfig struct { + onlineDDL viperutil.Value[bool] + directDDL viperutil.Value[bool] +} + +func (d *dynamicViperConfig) OnlineEnabled() bool { + return d.onlineDDL.Get() +} + +func (d *dynamicViperConfig) DirectEnabled() bool { + return d.directDDL.Get() +} diff --git a/go/vt/vtgate/vschema_manager.go b/go/vt/vtgate/vschema_manager.go index 2b6761f4a8e..62ea2cd3455 100644 --- a/go/vt/vtgate/vschema_manager.go +++ b/go/vt/vtgate/vschema_manager.go @@ -33,8 +33,6 @@ import ( vschemapb "vitess.io/vitess/go/vt/proto/vschema" ) -var _ VSchemaOperator = (*VSchemaManager)(nil) - // VSchemaManager is used to watch for updates to the vschema and to implement // the DDL commands to add / remove vindexes type VSchemaManager struct { diff --git a/go/vt/vtgate/vtgate.go b/go/vt/vtgate/vtgate.go index e9e7cd65011..8bab05479dd 100644 --- a/go/vt/vtgate/vtgate.go +++ b/go/vt/vtgate/vtgate.go @@ -34,6 +34,7 @@ import ( "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/stats" "vitess.io/vitess/go/tb" + "vitess.io/vitess/go/viperutil" "vitess.io/vitess/go/vt/discovery" "vitess.io/vitess/go/vt/key" "vitess.io/vitess/go/vt/log" @@ -51,6 +52,7 @@ import ( "vitess.io/vitess/go/vt/topo/topoproto" "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vterrors" + econtext "vitess.io/vitess/go/vt/vtgate/executorcontext" "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" vtschema "vitess.io/vitess/go/vt/vtgate/schema" "vitess.io/vitess/go/vt/vtgate/txresolver" @@ -93,8 +95,24 @@ var ( foreignKeyMode = "allow" dbDDLPlugin = "fail" defaultDDLStrategy = string(schema.DDLStrategyDirect) - enableOnlineDDL = true - enableDirectDDL = true + + enableOnlineDDL = viperutil.Configure( + "enable_online_ddl", + viperutil.Options[bool]{ + FlagName: "enable_online_ddl", + Default: true, + Dynamic: true, + }, + ) + + enableDirectDDL = viperutil.Configure( + "enable_direct_ddl", + viperutil.Options[bool]{ + FlagName: "enable_direct_ddl", + Default: true, + Dynamic: true, + }, + ) // schema tracking flags enableSchemaChangeSignal = true @@ -141,8 +159,8 @@ func registerFlags(fs *pflag.FlagSet) { fs.DurationVar(&lockHeartbeatTime, "lock_heartbeat_time", lockHeartbeatTime, "If there is lock function used. This will keep the lock connection active by using this heartbeat") fs.BoolVar(&warnShardedOnly, "warn_sharded_only", warnShardedOnly, "If any features that are only available in unsharded mode are used, query execution warnings will be added to the session") fs.StringVar(&foreignKeyMode, "foreign_key_mode", foreignKeyMode, "This is to provide how to handle foreign key constraint in create/alter table. Valid values are: allow, disallow") - fs.BoolVar(&enableOnlineDDL, "enable_online_ddl", enableOnlineDDL, "Allow users to submit, review and control Online DDL") - fs.BoolVar(&enableDirectDDL, "enable_direct_ddl", enableDirectDDL, "Allow users to submit direct DDL statements") + fs.Bool("enable_online_ddl", enableOnlineDDL.Default(), "Allow users to submit, review and control Online DDL") + fs.Bool("enable_direct_ddl", enableDirectDDL.Default(), "Allow users to submit direct DDL statements") fs.BoolVar(&enableSchemaChangeSignal, "schema_change_signal", enableSchemaChangeSignal, "Enable the schema tracker; requires queryserver-config-schema-change-signal to be enabled on the underlying vttablets for this to work") fs.IntVar(&queryTimeout, "query-timeout", queryTimeout, "Sets the default query timeout (in ms). Can be overridden by session variable (query_timeout) or comment directive (QUERY_TIMEOUT_MS)") fs.StringVar(&queryLogToFile, "log_queries_to_file", queryLogToFile, "Enable query logging to the specified file") @@ -154,6 +172,8 @@ func registerFlags(fs *pflag.FlagSet) { fs.IntVar(&warmingReadsPercent, "warming-reads-percent", 0, "Percentage of reads on the primary to forward to replicas. Useful for keeping buffer pools warm") fs.IntVar(&warmingReadsConcurrency, "warming-reads-concurrency", 500, "Number of concurrent warming reads allowed") fs.DurationVar(&warmingReadsQueryTimeout, "warming-reads-query-timeout", 5*time.Second, "Timeout of warming read queries") + + viperutil.BindFlags(fs, enableOnlineDDL, enableDirectDDL) } func init() { @@ -469,7 +489,7 @@ func (vtg *VTGate) Execute(ctx context.Context, mysqlCtx vtgateservice.MySQLConn if bvErr := sqltypes.ValidateBindVariables(bindVariables); bvErr != nil { err = vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "%v", bvErr) } else { - safeSession := NewSafeSession(session) + safeSession := econtext.NewSafeSession(session) qr, err = vtg.executor.Execute(ctx, mysqlCtx, "Execute", safeSession, sql, bindVariables) safeSession.RemoveInternalSavepoint() } @@ -526,7 +546,7 @@ func (vtg *VTGate) StreamExecute(ctx context.Context, mysqlCtx vtgateservice.MyS defer vtg.timings.Record(statsKey, time.Now()) - safeSession := NewSafeSession(session) + safeSession := econtext.NewSafeSession(session) var err error if bvErr := sqltypes.ValidateBindVariables(bindVariables); bvErr != nil { err = vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "%v", bvErr) @@ -560,7 +580,7 @@ func (vtg *VTGate) StreamExecute(ctx context.Context, mysqlCtx vtgateservice.MyS // same effect as if a "rollback" statement was executed, but does not affect the query // statistics. func (vtg *VTGate) CloseSession(ctx context.Context, session *vtgatepb.Session) error { - return vtg.executor.CloseSession(ctx, NewSafeSession(session)) + return vtg.executor.CloseSession(ctx, econtext.NewSafeSession(session)) } // Prepare supports non-streaming prepare statement query with multi shards @@ -575,7 +595,7 @@ func (vtg *VTGate) Prepare(ctx context.Context, session *vtgatepb.Session, sql s goto handleError } - fld, err = vtg.executor.Prepare(ctx, "Prepare", NewSafeSession(session), sql, bindVariables) + fld, err = vtg.executor.Prepare(ctx, "Prepare", econtext.NewSafeSession(session), sql, bindVariables) if err == nil { return session, fld, nil } diff --git a/go/vt/vtorc/config/config.go b/go/vt/vtorc/config/config.go index 2d21e377cb6..cafff5acce8 100644 --- a/go/vt/vtorc/config/config.go +++ b/go/vt/vtorc/config/config.go @@ -17,14 +17,12 @@ package config import ( - "encoding/json" - "fmt" - "os" "time" "github.com/spf13/pflag" - "vitess.io/vitess/go/vt/log" + "vitess.io/vitess/go/viperutil" + "vitess.io/vitess/go/vt/servenv" ) var configurationLoaded = make(chan bool) @@ -42,200 +40,296 @@ const ( ) var ( - sqliteDataFile = "file::memory:?mode=memory&cache=shared" - instancePollTime = 5 * time.Second - snapshotTopologyInterval = 0 * time.Hour - reasonableReplicationLag = 10 * time.Second - auditFileLocation = "" - auditToBackend = false - auditToSyslog = false - auditPurgeDuration = 7 * 24 * time.Hour // Equivalent of 7 days - recoveryPeriodBlockDuration = 30 * time.Second - preventCrossCellFailover = false - waitReplicasTimeout = 30 * time.Second - tolerableReplicationLag = 0 * time.Second - topoInformationRefreshDuration = 15 * time.Second - recoveryPollDuration = 1 * time.Second - ersEnabled = true - convertTabletsWithErrantGTIDs = false + instancePollTime = viperutil.Configure( + "instance-poll-time", + viperutil.Options[time.Duration]{ + FlagName: "instance-poll-time", + Default: 5 * time.Second, + Dynamic: true, + }, + ) + + preventCrossCellFailover = viperutil.Configure( + "prevent-cross-cell-failover", + viperutil.Options[bool]{ + FlagName: "prevent-cross-cell-failover", + Default: false, + Dynamic: true, + }, + ) + + sqliteDataFile = viperutil.Configure( + "sqlite-data-file", + viperutil.Options[string]{ + FlagName: "sqlite-data-file", + Default: "file::memory:?mode=memory&cache=shared", + Dynamic: false, + }, + ) + + snapshotTopologyInterval = viperutil.Configure( + "snapshot-topology-interval", + viperutil.Options[time.Duration]{ + FlagName: "snapshot-topology-interval", + Default: 0 * time.Hour, + Dynamic: true, + }, + ) + + reasonableReplicationLag = viperutil.Configure( + "reasonable-replication-lag", + viperutil.Options[time.Duration]{ + FlagName: "reasonable-replication-lag", + Default: 10 * time.Second, + Dynamic: true, + }, + ) + + auditFileLocation = viperutil.Configure( + "audit-file-location", + viperutil.Options[string]{ + FlagName: "audit-file-location", + Default: "", + Dynamic: false, + }, + ) + + auditToBackend = viperutil.Configure( + "audit-to-backend", + viperutil.Options[bool]{ + FlagName: "audit-to-backend", + Default: false, + Dynamic: true, + }, + ) + + auditToSyslog = viperutil.Configure( + "audit-to-syslog", + viperutil.Options[bool]{ + FlagName: "audit-to-syslog", + Default: false, + Dynamic: true, + }, + ) + + auditPurgeDuration = viperutil.Configure( + "audit-purge-duration", + viperutil.Options[time.Duration]{ + FlagName: "audit-purge-duration", + Default: 7 * 24 * time.Hour, + Dynamic: true, + }, + ) + + waitReplicasTimeout = viperutil.Configure( + "wait-replicas-timeout", + viperutil.Options[time.Duration]{ + FlagName: "wait-replicas-timeout", + Default: 30 * time.Second, + Dynamic: true, + }, + ) + + tolerableReplicationLag = viperutil.Configure( + "tolerable-replication-lag", + viperutil.Options[time.Duration]{ + FlagName: "tolerable-replication-lag", + Default: 0 * time.Second, + Dynamic: true, + }, + ) + + topoInformationRefreshDuration = viperutil.Configure( + "topo-information-refresh-duration", + viperutil.Options[time.Duration]{ + FlagName: "topo-information-refresh-duration", + Default: 15 * time.Second, + Dynamic: true, + }, + ) + + recoveryPollDuration = viperutil.Configure( + "recovery-poll-duration", + viperutil.Options[time.Duration]{ + FlagName: "recovery-poll-duration", + Default: 1 * time.Second, + Dynamic: true, + }, + ) + + ersEnabled = viperutil.Configure( + "allow-emergency-reparent", + viperutil.Options[bool]{ + FlagName: "allow-emergency-reparent", + Default: true, + Dynamic: true, + }, + ) + + convertTabletsWithErrantGTIDs = viperutil.Configure( + "change-tablets-with-errant-gtid-to-drained", + viperutil.Options[bool]{ + FlagName: "change-tablets-with-errant-gtid-to-drained", + Default: false, + Dynamic: true, + }, + ) ) -// RegisterFlags registers the flags required by VTOrc -func RegisterFlags(fs *pflag.FlagSet) { - fs.StringVar(&sqliteDataFile, "sqlite-data-file", sqliteDataFile, "SQLite Datafile to use as VTOrc's database") - fs.DurationVar(&instancePollTime, "instance-poll-time", instancePollTime, "Timer duration on which VTOrc refreshes MySQL information") - fs.DurationVar(&snapshotTopologyInterval, "snapshot-topology-interval", snapshotTopologyInterval, "Timer duration on which VTOrc takes a snapshot of the current MySQL information it has in the database. Should be in multiple of hours") - fs.DurationVar(&reasonableReplicationLag, "reasonable-replication-lag", reasonableReplicationLag, "Maximum replication lag on replicas which is deemed to be acceptable") - fs.StringVar(&auditFileLocation, "audit-file-location", auditFileLocation, "File location where the audit logs are to be stored") - fs.BoolVar(&auditToBackend, "audit-to-backend", auditToBackend, "Whether to store the audit log in the VTOrc database") - fs.BoolVar(&auditToSyslog, "audit-to-syslog", auditToSyslog, "Whether to store the audit log in the syslog") - fs.DurationVar(&auditPurgeDuration, "audit-purge-duration", auditPurgeDuration, "Duration for which audit logs are held before being purged. Should be in multiples of days") - fs.DurationVar(&recoveryPeriodBlockDuration, "recovery-period-block-duration", recoveryPeriodBlockDuration, "Duration for which a new recovery is blocked on an instance after running a recovery") - fs.MarkDeprecated("recovery-period-block-duration", "As of v20 this is ignored and will be removed in a future release.") - fs.BoolVar(&preventCrossCellFailover, "prevent-cross-cell-failover", preventCrossCellFailover, "Prevent VTOrc from promoting a primary in a different cell than the current primary in case of a failover") - fs.DurationVar(&waitReplicasTimeout, "wait-replicas-timeout", waitReplicasTimeout, "Duration for which to wait for replica's to respond when issuing RPCs") - fs.DurationVar(&tolerableReplicationLag, "tolerable-replication-lag", tolerableReplicationLag, "Amount of replication lag that is considered acceptable for a tablet to be eligible for promotion when Vitess makes the choice of a new primary in PRS") - fs.DurationVar(&topoInformationRefreshDuration, "topo-information-refresh-duration", topoInformationRefreshDuration, "Timer duration on which VTOrc refreshes the keyspace and vttablet records from the topology server") - fs.DurationVar(&recoveryPollDuration, "recovery-poll-duration", recoveryPollDuration, "Timer duration on which VTOrc polls its database to run a recovery") - fs.BoolVar(&ersEnabled, "allow-emergency-reparent", ersEnabled, "Whether VTOrc should be allowed to run emergency reparent operation when it detects a dead primary") - fs.BoolVar(&convertTabletsWithErrantGTIDs, "change-tablets-with-errant-gtid-to-drained", convertTabletsWithErrantGTIDs, "Whether VTOrc should be changing the type of tablets with errant GTIDs to DRAINED") +func init() { + servenv.OnParseFor("vtorc", registerFlags) } -// Configuration makes for vtorc configuration input, which can be provided by user via JSON formatted file. -// Some of the parameters have reasonable default values, and some (like database credentials) are -// strictly expected from user. -// TODO(sougou): change this to yaml parsing, and possible merge with tabletenv. -type Configuration struct { - SQLite3DataFile string // full path to sqlite3 datafile - InstancePollSeconds uint // Number of seconds between instance reads - SnapshotTopologiesIntervalHours uint // Interval in hour between snapshot-topologies invocation. Default: 0 (disabled) - ReasonableReplicationLagSeconds int // Above this value is considered a problem - AuditLogFile string // Name of log file for audit operations. Disabled when empty. - AuditToSyslog bool // If true, audit messages are written to syslog - AuditToBackendDB bool // If true, audit messages are written to the backend DB's `audit` table (default: true) - AuditPurgeDays uint // Days after which audit entries are purged from the database - RecoveryPeriodBlockSeconds int // (overrides `RecoveryPeriodBlockMinutes`) The time for which an instance's recovery is kept "active", so as to avoid concurrent recoveries on smae instance as well as flapping - PreventCrossDataCenterPrimaryFailover bool // When true (default: false), cross-DC primary failover are not allowed, vtorc will do all it can to only fail over within same DC, or else not fail over at all. - WaitReplicasTimeoutSeconds int // Timeout on amount of time to wait for the replicas in case of ERS. Should be a small value because we should fail-fast. Should not be larger than LockTimeout since that is the total time we use for an ERS. - TolerableReplicationLagSeconds int // Amount of replication lag that is considered acceptable for a tablet to be eligible for promotion when Vitess makes the choice of a new primary in PRS. - TopoInformationRefreshSeconds int // Timer duration on which VTOrc refreshes the keyspace and vttablet records from the topo-server. - RecoveryPollSeconds int // Timer duration on which VTOrc recovery analysis runs +// registerFlags registers the flags required by VTOrc +func registerFlags(fs *pflag.FlagSet) { + fs.String("sqlite-data-file", sqliteDataFile.Default(), "SQLite Datafile to use as VTOrc's database") + fs.Duration("instance-poll-time", instancePollTime.Default(), "Timer duration on which VTOrc refreshes MySQL information") + fs.Duration("snapshot-topology-interval", snapshotTopologyInterval.Default(), "Timer duration on which VTOrc takes a snapshot of the current MySQL information it has in the database. Should be in multiple of hours") + fs.Duration("reasonable-replication-lag", reasonableReplicationLag.Default(), "Maximum replication lag on replicas which is deemed to be acceptable") + fs.String("audit-file-location", auditFileLocation.Default(), "File location where the audit logs are to be stored") + fs.Bool("audit-to-backend", auditToBackend.Default(), "Whether to store the audit log in the VTOrc database") + fs.Bool("audit-to-syslog", auditToSyslog.Default(), "Whether to store the audit log in the syslog") + fs.Duration("audit-purge-duration", auditPurgeDuration.Default(), "Duration for which audit logs are held before being purged. Should be in multiples of days") + fs.Bool("prevent-cross-cell-failover", preventCrossCellFailover.Default(), "Prevent VTOrc from promoting a primary in a different cell than the current primary in case of a failover") + fs.Duration("wait-replicas-timeout", waitReplicasTimeout.Default(), "Duration for which to wait for replica's to respond when issuing RPCs") + fs.Duration("tolerable-replication-lag", tolerableReplicationLag.Default(), "Amount of replication lag that is considered acceptable for a tablet to be eligible for promotion when Vitess makes the choice of a new primary in PRS") + fs.Duration("topo-information-refresh-duration", topoInformationRefreshDuration.Default(), "Timer duration on which VTOrc refreshes the keyspace and vttablet records from the topology server") + fs.Duration("recovery-poll-duration", recoveryPollDuration.Default(), "Timer duration on which VTOrc polls its database to run a recovery") + fs.Bool("allow-emergency-reparent", ersEnabled.Default(), "Whether VTOrc should be allowed to run emergency reparent operation when it detects a dead primary") + fs.Bool("change-tablets-with-errant-gtid-to-drained", convertTabletsWithErrantGTIDs.Default(), "Whether VTOrc should be changing the type of tablets with errant GTIDs to DRAINED") + + viperutil.BindFlags(fs, + instancePollTime, + preventCrossCellFailover, + sqliteDataFile, + snapshotTopologyInterval, + reasonableReplicationLag, + auditFileLocation, + auditToBackend, + auditToSyslog, + auditPurgeDuration, + waitReplicasTimeout, + tolerableReplicationLag, + topoInformationRefreshDuration, + recoveryPollDuration, + ersEnabled, + convertTabletsWithErrantGTIDs, + ) } -// ToJSONString will marshal this configuration as JSON -func (config *Configuration) ToJSONString() string { - b, _ := json.Marshal(config) - return string(b) +// GetInstancePollTime is a getter function. +func GetInstancePollTime() time.Duration { + return instancePollTime.Get() } -// Config is *the* configuration instance, used globally to get configuration data -var Config = newConfiguration() -var readFileNames []string - -// UpdateConfigValuesFromFlags is used to update the config values from the flags defined. -// This is done before we read any configuration files from the user. So the config files take precedence. -func UpdateConfigValuesFromFlags() { - Config.SQLite3DataFile = sqliteDataFile - Config.InstancePollSeconds = uint(instancePollTime / time.Second) - Config.InstancePollSeconds = uint(instancePollTime / time.Second) - Config.SnapshotTopologiesIntervalHours = uint(snapshotTopologyInterval / time.Hour) - Config.ReasonableReplicationLagSeconds = int(reasonableReplicationLag / time.Second) - Config.AuditLogFile = auditFileLocation - Config.AuditToBackendDB = auditToBackend - Config.AuditToSyslog = auditToSyslog - Config.AuditPurgeDays = uint(auditPurgeDuration / (time.Hour * 24)) - Config.RecoveryPeriodBlockSeconds = int(recoveryPeriodBlockDuration / time.Second) - Config.PreventCrossDataCenterPrimaryFailover = preventCrossCellFailover - Config.WaitReplicasTimeoutSeconds = int(waitReplicasTimeout / time.Second) - Config.TolerableReplicationLagSeconds = int(tolerableReplicationLag / time.Second) - Config.TopoInformationRefreshSeconds = int(topoInformationRefreshDuration / time.Second) - Config.RecoveryPollSeconds = int(recoveryPollDuration / time.Second) +// SetInstancePollTime is a setter function. +func SetInstancePollTime(v time.Duration) { + instancePollTime.Set(v) } -// ERSEnabled reports whether VTOrc is allowed to run ERS or not. -func ERSEnabled() bool { - return ersEnabled +// GetInstancePollSeconds gets the instance poll time but in seconds. +func GetInstancePollSeconds() uint { + return uint(instancePollTime.Get() / time.Second) } -// SetERSEnabled sets the value for the ersEnabled variable. This should only be used from tests. -func SetERSEnabled(val bool) { - ersEnabled = val +// GetPreventCrossCellFailover is a getter function. +func GetPreventCrossCellFailover() bool { + return preventCrossCellFailover.Get() } -// ConvertTabletWithErrantGTIDs reports whether VTOrc is allowed to change the tablet type of tablets with errant GTIDs to DRAINED. -func ConvertTabletWithErrantGTIDs() bool { - return convertTabletsWithErrantGTIDs +// GetSQLiteDataFile is a getter function. +func GetSQLiteDataFile() string { + return sqliteDataFile.Get() } -// SetConvertTabletWithErrantGTIDs sets the value for the convertTabletWithErrantGTIDs variable. This should only be used from tests. -func SetConvertTabletWithErrantGTIDs(val bool) { - convertTabletsWithErrantGTIDs = val +// GetReasonableReplicationLagSeconds gets the reasonable replication lag but in seconds. +func GetReasonableReplicationLagSeconds() int64 { + return int64(reasonableReplicationLag.Get() / time.Second) +} + +// GetSnapshotTopologyInterval is a getter function. +func GetSnapshotTopologyInterval() time.Duration { + return snapshotTopologyInterval.Get() } -// LogConfigValues is used to log the config values. -func LogConfigValues() { - b, _ := json.MarshalIndent(Config, "", "\t") - log.Infof("Running with Configuration - %v", string(b)) +// GetAuditFileLocation is a getter function. +func GetAuditFileLocation() string { + return auditFileLocation.Get() } -func newConfiguration() *Configuration { - return &Configuration{ - SQLite3DataFile: "file::memory:?mode=memory&cache=shared", - InstancePollSeconds: 5, - SnapshotTopologiesIntervalHours: 0, - ReasonableReplicationLagSeconds: 10, - AuditLogFile: "", - AuditToSyslog: false, - AuditToBackendDB: false, - AuditPurgeDays: 7, - RecoveryPeriodBlockSeconds: 30, - PreventCrossDataCenterPrimaryFailover: false, - WaitReplicasTimeoutSeconds: 30, - TopoInformationRefreshSeconds: 15, - RecoveryPollSeconds: 1, - } +// SetAuditFileLocation is a setter function. +func SetAuditFileLocation(v string) { + auditFileLocation.Set(v) } -func (config *Configuration) postReadAdjustments() error { - if config.SQLite3DataFile == "" { - return fmt.Errorf("SQLite3DataFile must be set") - } +// GetAuditToSyslog is a getter function. +func GetAuditToSyslog() bool { + return auditToSyslog.Get() +} + +// SetAuditToSyslog is a setter function. +func SetAuditToSyslog(v bool) { + auditToSyslog.Set(v) +} + +// GetAuditToBackend is a getter function. +func GetAuditToBackend() bool { + return auditToBackend.Get() +} + +// SetAuditToBackend is a setter function. +func SetAuditToBackend(v bool) { + auditToBackend.Set(v) +} - return nil +// GetAuditPurgeDays gets the audit purge duration but in days. +func GetAuditPurgeDays() int64 { + return int64(auditPurgeDuration.Get() / (24 * time.Hour)) } -// read reads configuration from given file, or silently skips if the file does not exist. -// If the file does exist, then it is expected to be in valid JSON format or the function bails out. -func read(fileName string) (*Configuration, error) { - if fileName == "" { - return Config, fmt.Errorf("Empty file name") - } - file, err := os.Open(fileName) - if err != nil { - return Config, err - } - decoder := json.NewDecoder(file) - err = decoder.Decode(Config) - if err == nil { - log.Infof("Read config: %s", fileName) - } else { - log.Fatal("Cannot read config file:", fileName, err) - } - if err := Config.postReadAdjustments(); err != nil { - log.Fatal(err) - } - return Config, err +// SetAuditPurgeDays sets the audit purge duration. +func SetAuditPurgeDays(days int64) { + auditPurgeDuration.Set(time.Duration(days) * 24 * time.Hour) } -// Read reads configuration from zero, either, some or all given files, in order of input. -// A file can override configuration provided in previous file. -func Read(fileNames ...string) *Configuration { - for _, fileName := range fileNames { - _, _ = read(fileName) - } - readFileNames = fileNames - return Config +// GetWaitReplicasTimeout is a getter function. +func GetWaitReplicasTimeout() time.Duration { + return waitReplicasTimeout.Get() } -// ForceRead reads configuration from given file name or bails out if it fails -func ForceRead(fileName string) *Configuration { - _, err := read(fileName) - if err != nil { - log.Fatal("Cannot read config file:", fileName, err) - } - readFileNames = []string{fileName} - return Config +// GetTolerableReplicationLag is a getter function. +func GetTolerableReplicationLag() time.Duration { + return tolerableReplicationLag.Get() } -// Reload re-reads configuration from last used files -func Reload(extraFileNames ...string) *Configuration { - for _, fileName := range readFileNames { - _, _ = read(fileName) - } - for _, fileName := range extraFileNames { - _, _ = read(fileName) - } - return Config +// GetTopoInformationRefreshDuration is a getter function. +func GetTopoInformationRefreshDuration() time.Duration { + return topoInformationRefreshDuration.Get() +} + +// GetRecoveryPollDuration is a getter function. +func GetRecoveryPollDuration() time.Duration { + return recoveryPollDuration.Get() +} + +// ERSEnabled reports whether VTOrc is allowed to run ERS or not. +func ERSEnabled() bool { + return ersEnabled.Get() +} + +// SetERSEnabled sets the value for the ersEnabled variable. This should only be used from tests. +func SetERSEnabled(val bool) { + ersEnabled.Set(val) +} + +// ConvertTabletWithErrantGTIDs reports whether VTOrc is allowed to change the tablet type of tablets with errant GTIDs to DRAINED. +func ConvertTabletWithErrantGTIDs() bool { + return convertTabletsWithErrantGTIDs.Get() +} + +// SetConvertTabletWithErrantGTIDs sets the value for the convertTabletWithErrantGTIDs variable. This should only be used from tests. +func SetConvertTabletWithErrantGTIDs(val bool) { + convertTabletsWithErrantGTIDs.Set(val) } // MarkConfigurationLoaded is called once configuration has first been loaded. diff --git a/go/vt/vtorc/config/config_test.go b/go/vt/vtorc/config/config_test.go deleted file mode 100644 index 2009b476f1d..00000000000 --- a/go/vt/vtorc/config/config_test.go +++ /dev/null @@ -1,234 +0,0 @@ -/* -Copyright 2022 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 config - -import ( - "testing" - "time" - - "github.com/stretchr/testify/require" -) - -func TestUpdateConfigValuesFromFlags(t *testing.T) { - t.Run("defaults", func(t *testing.T) { - // Restore the changes we make to the Config parameter - defer func() { - Config = newConfiguration() - }() - defaultConfig := newConfiguration() - UpdateConfigValuesFromFlags() - require.Equal(t, defaultConfig, Config) - }) - - t.Run("override auditPurgeDuration", func(t *testing.T) { - oldAuditPurgeDuration := auditPurgeDuration - auditPurgeDuration = 8 * time.Hour * 24 - auditPurgeDuration += time.Second + 4*time.Minute - // Restore the changes we make - defer func() { - Config = newConfiguration() - auditPurgeDuration = oldAuditPurgeDuration - }() - - testConfig := newConfiguration() - // auditPurgeDuration is supposed to be in multiples of days. - // If it is not, then we round down to the nearest number of days. - testConfig.AuditPurgeDays = 8 - UpdateConfigValuesFromFlags() - require.Equal(t, testConfig, Config) - }) - - t.Run("override sqliteDataFile", func(t *testing.T) { - oldSqliteDataFile := sqliteDataFile - sqliteDataFile = "newVal" - // Restore the changes we make - defer func() { - Config = newConfiguration() - sqliteDataFile = oldSqliteDataFile - }() - - testConfig := newConfiguration() - testConfig.SQLite3DataFile = "newVal" - UpdateConfigValuesFromFlags() - require.Equal(t, testConfig, Config) - }) - - t.Run("override instancePollTime", func(t *testing.T) { - oldInstancePollTime := instancePollTime - instancePollTime = 7 * time.Second - // Restore the changes we make - defer func() { - Config = newConfiguration() - instancePollTime = oldInstancePollTime - }() - - testConfig := newConfiguration() - testConfig.InstancePollSeconds = 7 - UpdateConfigValuesFromFlags() - require.Equal(t, testConfig, Config) - }) - - t.Run("override snapshotTopologyInterval", func(t *testing.T) { - oldSnapshotTopologyInterval := snapshotTopologyInterval - snapshotTopologyInterval = 1 * time.Hour - // Restore the changes we make - defer func() { - Config = newConfiguration() - snapshotTopologyInterval = oldSnapshotTopologyInterval - }() - - testConfig := newConfiguration() - testConfig.SnapshotTopologiesIntervalHours = 1 - UpdateConfigValuesFromFlags() - require.Equal(t, testConfig, Config) - }) - - t.Run("override reasonableReplicationLag", func(t *testing.T) { - oldReasonableReplicationLag := reasonableReplicationLag - reasonableReplicationLag = 15 * time.Second - // Restore the changes we make - defer func() { - Config = newConfiguration() - reasonableReplicationLag = oldReasonableReplicationLag - }() - - testConfig := newConfiguration() - testConfig.ReasonableReplicationLagSeconds = 15 - UpdateConfigValuesFromFlags() - require.Equal(t, testConfig, Config) - }) - - t.Run("override auditFileLocation", func(t *testing.T) { - oldAuditFileLocation := auditFileLocation - auditFileLocation = "newFile" - // Restore the changes we make - defer func() { - Config = newConfiguration() - auditFileLocation = oldAuditFileLocation - }() - - testConfig := newConfiguration() - testConfig.AuditLogFile = "newFile" - UpdateConfigValuesFromFlags() - require.Equal(t, testConfig, Config) - }) - - t.Run("override auditToBackend", func(t *testing.T) { - oldAuditToBackend := auditToBackend - auditToBackend = true - // Restore the changes we make - defer func() { - Config = newConfiguration() - auditToBackend = oldAuditToBackend - }() - - testConfig := newConfiguration() - testConfig.AuditToBackendDB = true - UpdateConfigValuesFromFlags() - require.Equal(t, testConfig, Config) - }) - - t.Run("override auditToSyslog", func(t *testing.T) { - oldAuditToSyslog := auditToSyslog - auditToSyslog = true - // Restore the changes we make - defer func() { - Config = newConfiguration() - auditToSyslog = oldAuditToSyslog - }() - - testConfig := newConfiguration() - testConfig.AuditToSyslog = true - UpdateConfigValuesFromFlags() - require.Equal(t, testConfig, Config) - }) - - t.Run("override recoveryPeriodBlockDuration", func(t *testing.T) { - oldRecoveryPeriodBlockDuration := recoveryPeriodBlockDuration - recoveryPeriodBlockDuration = 5 * time.Minute - // Restore the changes we make - defer func() { - Config = newConfiguration() - recoveryPeriodBlockDuration = oldRecoveryPeriodBlockDuration - }() - - testConfig := newConfiguration() - testConfig.RecoveryPeriodBlockSeconds = 300 - UpdateConfigValuesFromFlags() - require.Equal(t, testConfig, Config) - }) - - t.Run("override preventCrossCellFailover", func(t *testing.T) { - oldPreventCrossCellFailover := preventCrossCellFailover - preventCrossCellFailover = true - // Restore the changes we make - defer func() { - Config = newConfiguration() - preventCrossCellFailover = oldPreventCrossCellFailover - }() - - testConfig := newConfiguration() - testConfig.PreventCrossDataCenterPrimaryFailover = true - UpdateConfigValuesFromFlags() - require.Equal(t, testConfig, Config) - }) - - t.Run("override waitReplicasTimeout", func(t *testing.T) { - oldWaitReplicasTimeout := waitReplicasTimeout - waitReplicasTimeout = 3*time.Minute + 4*time.Second - // Restore the changes we make - defer func() { - Config = newConfiguration() - waitReplicasTimeout = oldWaitReplicasTimeout - }() - - testConfig := newConfiguration() - testConfig.WaitReplicasTimeoutSeconds = 184 - UpdateConfigValuesFromFlags() - require.Equal(t, testConfig, Config) - }) - - t.Run("override topoInformationRefreshDuration", func(t *testing.T) { - oldTopoInformationRefreshDuration := topoInformationRefreshDuration - topoInformationRefreshDuration = 1 * time.Second - // Restore the changes we make - defer func() { - Config = newConfiguration() - topoInformationRefreshDuration = oldTopoInformationRefreshDuration - }() - - testConfig := newConfiguration() - testConfig.TopoInformationRefreshSeconds = 1 - UpdateConfigValuesFromFlags() - require.Equal(t, testConfig, Config) - }) - - t.Run("override recoveryPollDuration", func(t *testing.T) { - oldRecoveryPollDuration := recoveryPollDuration - recoveryPollDuration = 15 * time.Second - // Restore the changes we make - defer func() { - Config = newConfiguration() - recoveryPollDuration = oldRecoveryPollDuration - }() - - testConfig := newConfiguration() - testConfig.RecoveryPollSeconds = 15 - UpdateConfigValuesFromFlags() - require.Equal(t, testConfig, Config) - }) -} diff --git a/go/vt/vtorc/db/db.go b/go/vt/vtorc/db/db.go index 64143477645..870a3d15949 100644 --- a/go/vt/vtorc/db/db.go +++ b/go/vt/vtorc/db/db.go @@ -44,10 +44,12 @@ func (m *vtorcDB) QueryVTOrc(query string, argsArray []any, onRow func(sqlutils. // OpenTopology returns the DB instance for the vtorc backed database func OpenVTOrc() (db *sql.DB, err error) { var fromCache bool - db, fromCache, err = sqlutils.GetSQLiteDB(config.Config.SQLite3DataFile) + db, fromCache, err = sqlutils.GetSQLiteDB(config.GetSQLiteDataFile()) if err == nil && !fromCache { - log.Infof("Connected to vtorc backend: sqlite on %v", config.Config.SQLite3DataFile) - _ = initVTOrcDB(db) + log.Infof("Connected to vtorc backend: sqlite on %v", config.GetSQLiteDataFile()) + if err := initVTOrcDB(db); err != nil { + log.Fatalf("Cannot initiate vtorc: %+v", err) + } } if db != nil { db.SetMaxOpenConns(1) @@ -58,13 +60,13 @@ func OpenVTOrc() (db *sql.DB, err error) { // registerVTOrcDeployment updates the vtorc_db_deployments table upon successful deployment func registerVTOrcDeployment(db *sql.DB) error { - query := ` - replace into vtorc_db_deployments ( - deployed_version, deployed_timestamp - ) values ( - ?, datetime('now') - ) - ` + query := `REPLACE INTO vtorc_db_deployments ( + deployed_version, + deployed_timestamp + ) VALUES ( + ?, + DATETIME('now') + )` if _, err := execInternal(db, query, ""); err != nil { log.Fatalf("Unable to write to vtorc_db_deployments: %+v", err) } @@ -76,27 +78,24 @@ func registerVTOrcDeployment(db *sql.DB) error { func deployStatements(db *sql.DB, queries []string) error { tx, err := db.Begin() if err != nil { - log.Fatal(err.Error()) return err } for _, query := range queries { if _, err := tx.Exec(query); err != nil { - log.Fatalf("Cannot initiate vtorc: %+v; query=%+v", err, query) return err } } - if err := tx.Commit(); err != nil { - log.Fatal(err.Error()) - } - return nil + return tx.Commit() } // ClearVTOrcDatabase is used to clear the VTOrc database. This function is meant to be used by tests to clear the // database to get a clean slate without starting a new one. func ClearVTOrcDatabase() { - db, _, _ := sqlutils.GetSQLiteDB(config.Config.SQLite3DataFile) + db, _, _ := sqlutils.GetSQLiteDB(config.GetSQLiteDataFile()) if db != nil { - _ = initVTOrcDB(db) + if err := initVTOrcDB(db); err != nil { + log.Fatalf("Cannot re-initiate vtorc: %+v", err) + } } } @@ -105,20 +104,24 @@ func ClearVTOrcDatabase() { func initVTOrcDB(db *sql.DB) error { log.Info("Initializing vtorc") log.Info("Migrating database schema") - _ = deployStatements(db, vtorcBackend) - _ = registerVTOrcDeployment(db) - - _, _ = ExecVTOrc(`PRAGMA journal_mode = WAL`) - _, _ = ExecVTOrc(`PRAGMA synchronous = NORMAL`) - + if err := deployStatements(db, vtorcBackend); err != nil { + return err + } + if err := registerVTOrcDeployment(db); err != nil { + return err + } + if _, err := ExecVTOrc(`PRAGMA journal_mode = WAL`); err != nil { + return err + } + if _, err := ExecVTOrc(`PRAGMA synchronous = NORMAL`); err != nil { + return err + } return nil } // execInternal func execInternal(db *sql.DB, query string, args ...any) (sql.Result, error) { - var err error - res, err := sqlutils.ExecNoPrepare(db, query, args...) - return res, err + return sqlutils.ExecNoPrepare(db, query, args...) } // ExecVTOrc will execute given query on the vtorc backend database. diff --git a/go/vt/vtorc/discovery/queue.go b/go/vt/vtorc/discovery/queue.go index 95751c6ae25..4b18303959b 100644 --- a/go/vt/vtorc/discovery/queue.go +++ b/go/vt/vtorc/discovery/queue.go @@ -153,7 +153,7 @@ func (q *Queue) Consume() string { // alarm if have been waiting for too long timeOnQueue := time.Since(q.queuedKeys[key]) - if timeOnQueue > time.Duration(config.Config.InstancePollSeconds)*time.Second { + if timeOnQueue > config.GetInstancePollTime() { log.Warningf("key %v spent %.4fs waiting on a discoveryQueue", key, timeOnQueue.Seconds()) } diff --git a/go/vt/vtorc/inst/analysis.go b/go/vt/vtorc/inst/analysis.go index 66d6c6dd9ce..3e9e81c5c9f 100644 --- a/go/vt/vtorc/inst/analysis.go +++ b/go/vt/vtorc/inst/analysis.go @@ -144,5 +144,5 @@ func (replicationAnalysis *ReplicationAnalysis) MarshalJSON() ([]byte, error) { // ValidSecondsFromSeenToLastAttemptedCheck returns the maximum allowed elapsed time // between last_attempted_check to last_checked before we consider the instance as invalid. func ValidSecondsFromSeenToLastAttemptedCheck() uint { - return config.Config.InstancePollSeconds + 1 + return config.GetInstancePollSeconds() } diff --git a/go/vt/vtorc/inst/analysis_dao.go b/go/vt/vtorc/inst/analysis_dao.go index 25d93a6864b..07830bf7dda 100644 --- a/go/vt/vtorc/inst/analysis_dao.go +++ b/go/vt/vtorc/inst/analysis_dao.go @@ -47,7 +47,7 @@ func init() { func initializeAnalysisDaoPostConfiguration() { config.WaitForConfigurationToBeLoaded() - recentInstantAnalysis = cache.New(time.Duration(config.Config.RecoveryPollSeconds*2)*time.Second, time.Second) + recentInstantAnalysis = cache.New(config.GetRecoveryPollDuration()*2, time.Second) } type clusterAnalysis struct { @@ -68,9 +68,8 @@ func GetReplicationAnalysis(keyspace string, shard string, hints *ReplicationAna } // TODO(sougou); deprecate ReduceReplicationAnalysisCount - args := sqlutils.Args(config.Config.ReasonableReplicationLagSeconds, ValidSecondsFromSeenToLastAttemptedCheck(), config.Config.ReasonableReplicationLagSeconds, keyspace, shard) - query := ` - SELECT + args := sqlutils.Args(config.GetReasonableReplicationLagSeconds(), ValidSecondsFromSeenToLastAttemptedCheck(), config.GetReasonableReplicationLagSeconds(), keyspace, shard) + query := `SELECT vitess_tablet.info AS tablet_info, vitess_tablet.tablet_type, vitess_tablet.primary_timestamp, @@ -91,13 +90,13 @@ func GetReplicationAnalysis(keyspace string, shard string, hints *ReplicationAna IFNULL( primary_instance.binary_log_file = database_instance_stale_binlog_coordinates.binary_log_file AND primary_instance.binary_log_pos = database_instance_stale_binlog_coordinates.binary_log_pos - AND database_instance_stale_binlog_coordinates.first_seen < datetime('now', printf('-%d second', ?)), + AND database_instance_stale_binlog_coordinates.first_seen < DATETIME('now', PRINTF('-%d SECOND', ?)), 0 ) ) AS is_stale_binlog_coordinates, MIN( primary_instance.last_checked <= primary_instance.last_seen - and primary_instance.last_attempted_check <= datetime(primary_instance.last_seen, printf('+%d second', ?)) + and primary_instance.last_attempted_check <= DATETIME(primary_instance.last_seen, PRINTF('+%d SECOND', ?)) ) = 1 AS is_last_check_valid, /* To be considered a primary, traditional async replication must not be present/valid AND the host should either */ /* not be a replication group member OR be the primary of the replication group */ @@ -655,13 +654,13 @@ func auditInstanceAnalysisInChangelog(tabletAlias string, analysisCode AnalysisC // Find if the lastAnalysisHasChanged or not while updating the row if it has. lastAnalysisChanged := false { - sqlResult, err := db.ExecVTOrc(` - update database_instance_last_analysis set + sqlResult, err := db.ExecVTOrc(`UPDATE database_instance_last_analysis + SET analysis = ?, - analysis_timestamp = datetime('now') - where + analysis_timestamp = DATETIME('now') + WHERE alias = ? - and analysis != ? + AND analysis != ? `, string(analysisCode), tabletAlias, string(analysisCode), ) @@ -682,13 +681,16 @@ func auditInstanceAnalysisInChangelog(tabletAlias string, analysisCode AnalysisC firstInsertion := false if !lastAnalysisChanged { // The insert only returns more than 1 row changed if this is the first insertion. - sqlResult, err := db.ExecVTOrc(` - insert or ignore into database_instance_last_analysis ( - alias, analysis_timestamp, analysis - ) values ( - ?, datetime('now'), ? - ) - `, + sqlResult, err := db.ExecVTOrc(`INSERT OR IGNORE + INTO database_instance_last_analysis ( + alias, + analysis_timestamp, + analysis + ) VALUES ( + ?, + DATETIME('now'), + ? + )`, tabletAlias, string(analysisCode), ) if err != nil { @@ -708,13 +710,16 @@ func auditInstanceAnalysisInChangelog(tabletAlias string, analysisCode AnalysisC return nil } - _, err := db.ExecVTOrc(` - insert into database_instance_analysis_changelog ( - alias, analysis_timestamp, analysis - ) values ( - ?, datetime('now'), ? - ) - `, + _, err := db.ExecVTOrc(`INSERT + INTO database_instance_analysis_changelog ( + alias, + analysis_timestamp, + analysis + ) VALUES ( + ?, + DATETIME('now'), + ? + )`, tabletAlias, string(analysisCode), ) if err == nil { @@ -727,12 +732,11 @@ func auditInstanceAnalysisInChangelog(tabletAlias string, analysisCode AnalysisC // ExpireInstanceAnalysisChangelog removes old-enough analysis entries from the changelog func ExpireInstanceAnalysisChangelog() error { - _, err := db.ExecVTOrc(` - delete - from database_instance_analysis_changelog - where - analysis_timestamp < datetime('now', printf('-%d hour', ?)) - `, + _, err := db.ExecVTOrc(`DELETE + FROM database_instance_analysis_changelog + WHERE + analysis_timestamp < DATETIME('now', PRINTF('-%d HOUR', ?)) + `, config.UnseenInstanceForgetHours, ) if err != nil { diff --git a/go/vt/vtorc/inst/audit_dao.go b/go/vt/vtorc/inst/audit_dao.go index 642fb187509..7ae60fba927 100644 --- a/go/vt/vtorc/inst/audit_dao.go +++ b/go/vt/vtorc/inst/audit_dao.go @@ -38,10 +38,10 @@ func AuditOperation(auditType string, tabletAlias string, message string) error } auditWrittenToFile := false - if config.Config.AuditLogFile != "" { + if config.GetAuditFileLocation() != "" { auditWrittenToFile = true go func() { - f, err := os.OpenFile(config.Config.AuditLogFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0640) + f, err := os.OpenFile(config.GetAuditFileLocation(), os.O_RDWR|os.O_CREATE|os.O_APPEND, 0640) if err != nil { log.Error(err) return @@ -54,15 +54,23 @@ func AuditOperation(auditType string, tabletAlias string, message string) error } }() } - if config.Config.AuditToBackendDB { - _, err := db.ExecVTOrc(` - insert - into audit ( - audit_timestamp, audit_type, alias, keyspace, shard, message - ) VALUES ( - datetime('now'), ?, ?, ?, ?, ? - ) - `, + if config.GetAuditToBackend() { + _, err := db.ExecVTOrc(`INSERT + INTO audit ( + audit_timestamp, + audit_type, + alias, + keyspace, + shard, + message + ) VALUES ( + DATETIME('now'), + ?, + ?, + ?, + ?, + ? + )`, auditType, tabletAlias, keyspace, diff --git a/go/vt/vtorc/inst/audit_dao_test.go b/go/vt/vtorc/inst/audit_dao_test.go index 1d50de4c146..d22e9177dc3 100644 --- a/go/vt/vtorc/inst/audit_dao_test.go +++ b/go/vt/vtorc/inst/audit_dao_test.go @@ -35,13 +35,13 @@ import ( // This test also verifies that we are able to read the recent audits that are written to the databaes. func TestAuditOperation(t *testing.T) { // Restore original configurations - originalAuditSysLog := config.Config.AuditToSyslog - originalAuditLogFile := config.Config.AuditLogFile - originalAuditBackend := config.Config.AuditToBackendDB + originalAuditSysLog := config.GetAuditToSyslog() + originalAuditLogFile := config.GetAuditFileLocation() + originalAuditBackend := config.GetAuditToBackend() defer func() { - config.Config.AuditToSyslog = originalAuditSysLog - config.Config.AuditLogFile = originalAuditLogFile - config.Config.AuditToBackendDB = originalAuditBackend + config.SetAuditToSyslog(originalAuditSysLog) + config.SetAuditFileLocation(originalAuditLogFile) + config.SetAuditToBackend(originalAuditBackend) }() orcDb, err := db.OpenVTOrc() @@ -78,9 +78,9 @@ func TestAuditOperation(t *testing.T) { message := "test-message" t.Run("audit to backend", func(t *testing.T) { - config.Config.AuditLogFile = "" - config.Config.AuditToSyslog = false - config.Config.AuditToBackendDB = true + config.SetAuditFileLocation("") + config.SetAuditToSyslog(false) + config.SetAuditToBackend(true) // Auditing should succeed as expected err = AuditOperation(auditType, tab100Alias, message) @@ -106,13 +106,13 @@ func TestAuditOperation(t *testing.T) { }) t.Run("audit to File", func(t *testing.T) { - config.Config.AuditToBackendDB = false - config.Config.AuditToSyslog = false + config.SetAuditToBackend(false) + config.SetAuditToSyslog(false) file, err := os.CreateTemp("", "test-auditing-*") require.NoError(t, err) defer os.Remove(file.Name()) - config.Config.AuditLogFile = file.Name() + config.SetAuditFileLocation(file.Name()) err = AuditOperation(auditType, tab100Alias, message) require.NoError(t, err) diff --git a/go/vt/vtorc/inst/instance_dao.go b/go/vt/vtorc/inst/instance_dao.go index bd4438dd05f..d1421dbc91d 100644 --- a/go/vt/vtorc/inst/instance_dao.go +++ b/go/vt/vtorc/inst/instance_dao.go @@ -80,7 +80,7 @@ func init() { func initializeInstanceDao() { config.WaitForConfigurationToBeLoaded() - forgetAliases = cache.New(time.Duration(config.Config.InstancePollSeconds*3)*time.Second, time.Second) + forgetAliases = cache.New(config.GetInstancePollTime()*3, time.Second) cacheInitializationCompleted.Store(true) } @@ -114,10 +114,15 @@ func ExecDBWriteFunc(f func() error) error { func ExpireTableData(tableName string, timestampColumn string) error { writeFunc := func() error { - _, err := db.ExecVTOrc( - fmt.Sprintf("delete from %s where %s < datetime('now', printf('-%%d DAY', ?))", tableName, timestampColumn), - config.Config.AuditPurgeDays, + query := fmt.Sprintf(`DELETE + FROM %s + WHERE + %s < DATETIME('now', PRINTF('-%%d DAY', ?)) + `, + tableName, + timestampColumn, ) + _, err := db.ExecVTOrc(query, config.GetAuditPurgeDays()) return err } return ExecDBWriteFunc(writeFunc) @@ -357,35 +362,7 @@ Cleanup: // Add replication group ancestry UUID as well. Otherwise, VTOrc thinks there are errant GTIDs in group // members and its replicas, even though they are not. instance.AncestryUUID = strings.Trim(instance.AncestryUUID, ",") - if instance.ExecutedGtidSet != "" && instance.primaryExecutedGtidSet != "" { - // Compare primary & replica GTID sets, but ignore the sets that present the primary's UUID. - // This is because vtorc may pool primary and replica at an inconvenient timing, - // such that the replica may _seems_ to have more entries than the primary, when in fact - // it's just that the primary's probing is stale. - redactedExecutedGtidSet, _ := NewOracleGtidSet(instance.ExecutedGtidSet) - for _, uuid := range strings.Split(instance.AncestryUUID, ",") { - if uuid != instance.ServerUUID { - redactedExecutedGtidSet.RemoveUUID(uuid) - } - if instance.IsCoPrimary && uuid == instance.ServerUUID { - // If this is a co-primary, then this server is likely to show its own generated GTIDs as errant, - // because its co-primary has not applied them yet - redactedExecutedGtidSet.RemoveUUID(uuid) - } - } - // Avoid querying the database if there's no point: - if !redactedExecutedGtidSet.IsEmpty() { - redactedPrimaryExecutedGtidSet, _ := NewOracleGtidSet(instance.primaryExecutedGtidSet) - redactedPrimaryExecutedGtidSet.RemoveUUID(instance.SourceUUID) - - instance.GtidErrant, err = replication.Subtract(redactedExecutedGtidSet.String(), redactedPrimaryExecutedGtidSet.String()) - if err == nil { - var gtidCount int64 - gtidCount, err = replication.GTIDCount(instance.GtidErrant) - currentErrantGTIDCount.Set(tabletAlias, gtidCount) - } - } - } + err = detectErrantGTIDs(instance, tablet) } latency.Stop("instance") @@ -412,6 +389,63 @@ Cleanup: return nil, err } +// detectErrantGTIDs detects the errant GTIDs on an instance. +func detectErrantGTIDs(instance *Instance, tablet *topodatapb.Tablet) (err error) { + // If the tablet is not replicating from anyone, then it could be the previous primary. + // We should check for errant GTIDs by finding the difference with the shard's current primary. + if instance.primaryExecutedGtidSet == "" && instance.SourceHost == "" { + var primaryInstance *Instance + primaryAlias, _, _ := ReadShardPrimaryInformation(tablet.Keyspace, tablet.Shard) + if primaryAlias != "" { + // Check if the current tablet is the primary. + // If it is, then we don't need to run errant gtid detection on it. + if primaryAlias == instance.InstanceAlias { + return nil + } + primaryInstance, _, _ = ReadInstance(primaryAlias) + } + // Only run errant GTID detection, if we are sure that the data read of the current primary + // is up-to-date enough to reflect that it has been promoted. This is needed to prevent + // flagging incorrect errant GTIDs. If we were to use old data, we could have some GTIDs + // accepted by the old primary (this tablet) that don't show in the new primary's set. + if primaryInstance != nil { + if primaryInstance.SourceHost == "" { + instance.primaryExecutedGtidSet = primaryInstance.ExecutedGtidSet + } + } + } + if instance.ExecutedGtidSet != "" && instance.primaryExecutedGtidSet != "" { + // Compare primary & replica GTID sets, but ignore the sets that present the primary's UUID. + // This is because vtorc may pool primary and replica at an inconvenient timing, + // such that the replica may _seems_ to have more entries than the primary, when in fact + // it's just that the primary's probing is stale. + redactedExecutedGtidSet, _ := NewOracleGtidSet(instance.ExecutedGtidSet) + for _, uuid := range strings.Split(instance.AncestryUUID, ",") { + if uuid != instance.ServerUUID { + redactedExecutedGtidSet.RemoveUUID(uuid) + } + if instance.IsCoPrimary && uuid == instance.ServerUUID { + // If this is a co-primary, then this server is likely to show its own generated GTIDs as errant, + // because its co-primary has not applied them yet + redactedExecutedGtidSet.RemoveUUID(uuid) + } + } + // Avoid querying the database if there's no point: + if !redactedExecutedGtidSet.IsEmpty() { + redactedPrimaryExecutedGtidSet, _ := NewOracleGtidSet(instance.primaryExecutedGtidSet) + redactedPrimaryExecutedGtidSet.RemoveUUID(instance.SourceUUID) + + instance.GtidErrant, err = replication.Subtract(redactedExecutedGtidSet.String(), redactedPrimaryExecutedGtidSet.String()) + if err == nil { + var gtidCount int64 + gtidCount, err = replication.GTIDCount(instance.GtidErrant) + currentErrantGTIDCount.Set(instance.InstanceAlias, gtidCount) + } + } + } + return err +} + // getKeyspaceShardName returns a single string having both the keyspace and shard func getKeyspaceShardName(keyspace, shard string) string { return fmt.Sprintf("%v:%v", keyspace, shard) @@ -439,16 +473,16 @@ func ReadInstanceClusterAttributes(instance *Instance) (err error) { var primaryExecutedGtidSet string primaryDataFound := false - query := ` - select - replication_depth, - source_host, - source_port, - ancestry_uuid, - executed_gtid_set - from database_instance - where hostname=? and port=? - ` + query := `SELECT + replication_depth, + source_host, + source_port, + ancestry_uuid, + executed_gtid_set + FROM database_instance + WHERE + hostname = ? + AND port = ?` primaryHostname := instance.SourceHost primaryPort := instance.SourcePort args := sqlutils.Args(primaryHostname, primaryPort) @@ -544,8 +578,8 @@ func readInstanceRow(m sqlutils.RowMap) *Instance { instance.ReplicationDepth = m.GetUint("replication_depth") instance.IsCoPrimary = m.GetBool("is_co_primary") instance.HasReplicationCredentials = m.GetBool("has_replication_credentials") - instance.IsUpToDate = (m.GetUint("seconds_since_last_checked") <= config.Config.InstancePollSeconds) - instance.IsRecentlyChecked = (m.GetUint("seconds_since_last_checked") <= config.Config.InstancePollSeconds*5) + instance.IsUpToDate = m.GetUint("seconds_since_last_checked") <= config.GetInstancePollSeconds() + instance.IsRecentlyChecked = m.GetUint("seconds_since_last_checked") <= config.GetInstancePollSeconds()*5 instance.LastSeenTimestamp = m.GetString("last_seen") instance.IsLastCheckValid = m.GetBool("is_last_check_valid") instance.SecondsSinceLastSeen = m.GetNullInt64("seconds_since_last_seen") @@ -562,7 +596,7 @@ func readInstanceRow(m sqlutils.RowMap) *Instance { instance.Problems = append(instance.Problems, "not_recently_checked") } else if instance.ReplicationThreadsExist() && !instance.ReplicaRunning() { instance.Problems = append(instance.Problems, "not_replicating") - } else if instance.ReplicationLagSeconds.Valid && util.AbsInt64(instance.ReplicationLagSeconds.Int64-int64(instance.SQLDelay)) > int64(config.Config.ReasonableReplicationLagSeconds) { + } else if instance.ReplicationLagSeconds.Valid && util.AbsInt64(instance.ReplicationLagSeconds.Int64-int64(instance.SQLDelay)) > int64(config.GetReasonableReplicationLagSeconds()) { instance.Problems = append(instance.Problems, "replication_lag") } if instance.GtidErrant != "" { @@ -580,20 +614,22 @@ func readInstancesByCondition(condition string, args []any, sort string) ([](*In if sort == "" { sort = `alias` } - query := fmt.Sprintf(` - select - *, - strftime('%%s', 'now') - strftime('%%s', last_checked) as seconds_since_last_checked, - ifnull(last_checked <= last_seen, 0) as is_last_check_valid, - strftime('%%s', 'now') - strftime('%%s', last_seen) as seconds_since_last_seen - from - vitess_tablet - left join database_instance using (alias, hostname, port) - where - %s - order by - %s - `, condition, sort) + query := fmt.Sprintf(`SELECT + *, + STRFTIME('%%s', 'now') - STRFTIME('%%s', last_checked) AS seconds_since_last_checked, + IFNULL(last_checked <= last_seen, 0) AS is_last_check_valid, + STRFTIME('%%s', 'now') - STRFTIME('%%s', last_seen) AS seconds_since_last_seen + FROM + vitess_tablet + LEFT JOIN database_instance USING (alias, hostname, port) + WHERE + %s + ORDER BY + %s + `, + condition, + sort, + ) err := db.QueryVTOrc(query, args, func(m sqlutils.RowMap) error { instance := readInstanceRow(m) @@ -614,9 +650,7 @@ func readInstancesByCondition(condition string, args []any, sort string) ([](*In // ReadInstance reads an instance from the vtorc backend database func ReadInstance(tabletAlias string) (*Instance, bool, error) { - condition := ` - alias = ? - ` + condition := `alias = ?` instances, err := readInstancesByCondition(condition, sqlutils.Args(tabletAlias), "") // We know there will be at most one (alias is the PK). // And we expect to find one. @@ -633,30 +667,28 @@ func ReadInstance(tabletAlias string) (*Instance, bool, error) { // ReadProblemInstances reads all instances with problems func ReadProblemInstances(keyspace string, shard string) ([](*Instance), error) { condition := ` - keyspace LIKE (CASE WHEN ? = '' THEN '%' ELSE ? END) - and shard LIKE (CASE WHEN ? = '' THEN '%' ELSE ? END) - and ( - (last_seen < last_checked) - or (strftime('%%s', 'now') - strftime('%%s', last_checked) > ?) - or (replication_sql_thread_state not in (-1 ,1)) - or (replication_io_thread_state not in (-1 ,1)) - or (abs(cast(replication_lag_seconds as integer) - cast(sql_delay as integer)) > ?) - or (abs(cast(replica_lag_seconds as integer) - cast(sql_delay as integer)) > ?) - or (gtid_errant != '') - ) - ` - - args := sqlutils.Args(keyspace, keyspace, shard, shard, config.Config.InstancePollSeconds*5, config.Config.ReasonableReplicationLagSeconds, config.Config.ReasonableReplicationLagSeconds) + keyspace LIKE (CASE WHEN ? = '' THEN '%' ELSE ? END) + AND shard LIKE (CASE WHEN ? = '' THEN '%' ELSE ? END) + AND ( + (last_seen < last_checked) + OR (STRFTIME('%%s', 'now') - STRFTIME('%%s', last_checked) > ?) + OR (replication_sql_thread_state NOT IN (-1 ,1)) + OR (replication_io_thread_state NOT IN (-1 ,1)) + OR (ABS(CAST(replication_lag_seconds AS integer) - CAST(sql_delay AS integer)) > ?) + OR (ABS(CAST(replica_lag_seconds AS integer) - CAST(sql_delay AS integer)) > ?) + OR (gtid_errant != '') + )` + + args := sqlutils.Args(keyspace, keyspace, shard, shard, config.GetInstancePollSeconds()*5, config.GetReasonableReplicationLagSeconds(), config.GetReasonableReplicationLagSeconds()) return readInstancesByCondition(condition, args, "") } // ReadInstancesWithErrantGTIds reads all instances with errant GTIDs func ReadInstancesWithErrantGTIds(keyspace string, shard string) ([]*Instance, error) { condition := ` - keyspace LIKE (CASE WHEN ? = '' THEN '%' ELSE ? END) - and shard LIKE (CASE WHEN ? = '' THEN '%' ELSE ? END) - and gtid_errant != '' - ` + keyspace LIKE (CASE WHEN ? = '' THEN '%' ELSE ? END) + AND shard LIKE (CASE WHEN ? = '' THEN '%' ELSE ? END) + AND gtid_errant != ''` args := sqlutils.Args(keyspace, keyspace, shard, shard) return readInstancesByCondition(condition, args, "") @@ -664,15 +696,14 @@ func ReadInstancesWithErrantGTIds(keyspace string, shard string) ([]*Instance, e // GetKeyspaceShardName gets the keyspace shard name for the given instance key func GetKeyspaceShardName(tabletAlias string) (keyspace string, shard string, err error) { - query := ` - select - keyspace, - shard - from - vitess_tablet - where - alias = ? - ` + query := `SELECT + keyspace, + shard + FROM + vitess_tablet + WHERE + alias = ? + ` err = db.QueryVTOrc(query, sqlutils.Args(tabletAlias), func(m sqlutils.RowMap) error { keyspace = m.GetString("keyspace") shard = m.GetString("shard") @@ -695,28 +726,27 @@ func GetKeyspaceShardName(tabletAlias string) (keyspace string, shard string, er // the instance. func ReadOutdatedInstanceKeys() ([]string, error) { var res []string - query := ` - SELECT - alias - FROM - database_instance - WHERE - CASE - WHEN last_attempted_check <= last_checked - THEN last_checked < datetime('now', printf('-%d second', ?)) - ELSE last_checked < datetime('now', printf('-%d second', ?)) - END - UNION - SELECT - vitess_tablet.alias - FROM - vitess_tablet LEFT JOIN database_instance ON ( - vitess_tablet.alias = database_instance.alias - ) - WHERE - database_instance.alias IS NULL - ` - args := sqlutils.Args(config.Config.InstancePollSeconds, 2*config.Config.InstancePollSeconds) + query := `SELECT + alias + FROM + database_instance + WHERE + CASE + WHEN last_attempted_check <= last_checked + THEN last_checked < DATETIME('now', PRINTF('-%d SECOND', ?)) + ELSE last_checked < DATETIME('now', PRINTF('-%d SECOND', ?)) + END + UNION + SELECT + vitess_tablet.alias + FROM + vitess_tablet LEFT JOIN database_instance ON ( + vitess_tablet.alias = database_instance.alias + ) + WHERE + database_instance.alias IS NULL + ` + args := sqlutils.Args(config.GetInstancePollSeconds(), 2*config.GetInstancePollSeconds()) err := db.QueryVTOrc(query, args, func(m sqlutils.RowMap) error { tabletAlias := m.GetString("alias") @@ -758,12 +788,17 @@ func mkInsert(table string, columns []string, values []string, nrRows int, inser } col := strings.Join(columns, ", ") - q.WriteString(fmt.Sprintf(`%s %s - (%s) - VALUES - %s - `, - insertStr, table, col, val.String())) + query := fmt.Sprintf(`%s %s + (%s) + VALUES + %s + `, + insertStr, + table, + col, + val.String(), + ) + q.WriteString(query) return q.String(), nil } @@ -849,13 +884,13 @@ func mkInsertForInstances(instances []*Instance, instanceWasActuallyFound bool, for i := range columns { values[i] = "?" } - values[3] = "datetime('now')" // last_checked - values[4] = "datetime('now')" // last_attempted_check + values[3] = "DATETIME('now')" // last_checked + values[4] = "DATETIME('now')" // last_attempted_check values[5] = "1" // last_check_partial_success if updateLastSeen { columns = append(columns, "last_seen") - values = append(values, "datetime('now')") + values = append(values, "DATETIME('now')") } var args []any @@ -971,14 +1006,13 @@ func WriteInstance(instance *Instance, instanceWasActuallyFound bool, lastError // for a given instance func UpdateInstanceLastChecked(tabletAlias string, partialSuccess bool) error { writeFunc := func() error { - _, err := db.ExecVTOrc(` - update - database_instance - set - last_checked = datetime('now'), - last_check_partial_success = ? - where - alias = ?`, + _, err := db.ExecVTOrc(`UPDATE database_instance + SET + last_checked = DATETIME('now'), + last_check_partial_success = ? + WHERE + alias = ? + `, partialSuccess, tabletAlias, ) @@ -1000,13 +1034,12 @@ func UpdateInstanceLastChecked(tabletAlias string, partialSuccess bool) error { // we have a "hanging" issue. func UpdateInstanceLastAttemptedCheck(tabletAlias string) error { writeFunc := func() error { - _, err := db.ExecVTOrc(` - update - database_instance - set - last_attempted_check = datetime('now') - where - alias = ?`, + _, err := db.ExecVTOrc(`UPDATE database_instance + SET + last_attempted_check = DATETIME('now') + WHERE + alias = ? + `, tabletAlias, ) if err != nil { @@ -1037,11 +1070,11 @@ func ForgetInstance(tabletAlias string) error { currentErrantGTIDCount.Reset(tabletAlias) // Delete from the 'vitess_tablet' table. - _, err := db.ExecVTOrc(` - delete - from vitess_tablet - where - alias = ?`, + _, err := db.ExecVTOrc(`DELETE + FROM vitess_tablet + WHERE + alias = ? + `, tabletAlias, ) if err != nil { @@ -1050,11 +1083,11 @@ func ForgetInstance(tabletAlias string) error { } // Also delete from the 'database_instance' table. - sqlResult, err := db.ExecVTOrc(` - delete - from database_instance - where - alias = ?`, + sqlResult, err := db.ExecVTOrc(`DELETE + FROM database_instance + WHERE + alias = ? + `, tabletAlias, ) if err != nil { @@ -1078,11 +1111,11 @@ func ForgetInstance(tabletAlias string) error { // ForgetLongUnseenInstances will remove entries of all instances that have long since been last seen. func ForgetLongUnseenInstances() error { - sqlResult, err := db.ExecVTOrc(` - delete - from database_instance - where - last_seen < datetime('now', printf('-%d hour', ?))`, + sqlResult, err := db.ExecVTOrc(`DELETE + FROM database_instance + WHERE + last_seen < DATETIME('now', PRINTF('-%d HOUR', ?)) + `, config.UnseenInstanceForgetHours, ) if err != nil { @@ -1103,18 +1136,26 @@ func ForgetLongUnseenInstances() error { // SnapshotTopologies records topology graph for all existing topologies func SnapshotTopologies() error { writeFunc := func() error { - _, err := db.ExecVTOrc(` - insert or ignore into - database_instance_topology_history (snapshot_unix_timestamp, - alias, hostname, port, source_host, source_port, keyspace, shard, version) - select - strftime('%s', 'now'), - vitess_tablet.alias, vitess_tablet.hostname, vitess_tablet.port, - database_instance.source_host, database_instance.source_port, + _, err := db.ExecVTOrc(`INSERT OR IGNORE + INTO database_instance_topology_history ( + snapshot_unix_timestamp, + alias, + hostname, + port, + source_host, + source_port, + keyspace, + shard, + version + ) + SELECT + STRFTIME('%s', 'now'), + vitess_tablet.alias, vitess_tablet.hostname, vitess_tablet.port, + database_instance.source_host, database_instance.source_port, vitess_tablet.keyspace, vitess_tablet.shard, database_instance.version - from - vitess_tablet left join database_instance using (alias, hostname, port) - `, + FROM + vitess_tablet LEFT JOIN database_instance USING (alias, hostname, port) + `, ) if err != nil { log.Error(err) @@ -1127,15 +1168,17 @@ func SnapshotTopologies() error { } func ExpireStaleInstanceBinlogCoordinates() error { - expireSeconds := config.Config.ReasonableReplicationLagSeconds * 2 + expireSeconds := config.GetReasonableReplicationLagSeconds() * 2 if expireSeconds < config.StaleInstanceCoordinatesExpireSeconds { expireSeconds = config.StaleInstanceCoordinatesExpireSeconds } writeFunc := func() error { - _, err := db.ExecVTOrc(` - delete from database_instance_stale_binlog_coordinates - where first_seen < datetime('now', printf('-%d second', ?)) - `, expireSeconds, + _, err := db.ExecVTOrc(`DELETE + FROM database_instance_stale_binlog_coordinates + WHERE + first_seen < DATETIME('now', PRINTF('-%d SECOND', ?)) + `, + expireSeconds, ) if err != nil { log.Error(err) @@ -1157,7 +1200,7 @@ func GetDatabaseState() (string, error) { ts := tableState{ TableName: tableName, } - err := db.QueryVTOrc("select * from "+tableName, nil, func(rowMap sqlutils.RowMap) error { + err := db.QueryVTOrc("SELECT * FROM "+tableName, nil, func(rowMap sqlutils.RowMap) error { ts.Rows = append(ts.Rows, rowMap) return nil }) diff --git a/go/vt/vtorc/inst/instance_dao_test.go b/go/vt/vtorc/inst/instance_dao_test.go index 2416c1abb90..cc3217442ed 100644 --- a/go/vt/vtorc/inst/instance_dao_test.go +++ b/go/vt/vtorc/inst/instance_dao_test.go @@ -14,6 +14,7 @@ import ( "vitess.io/vitess/go/vt/external/golib/sqlutils" "vitess.io/vitess/go/vt/log" topodatapb "vitess.io/vitess/go/vt/proto/topodata" + "vitess.io/vitess/go/vt/topo" "vitess.io/vitess/go/vt/topo/topoproto" "vitess.io/vitess/go/vt/vtorc/config" "vitess.io/vitess/go/vt/vtorc/db" @@ -65,7 +66,7 @@ func TestMkInsertSingle(t *testing.T) { replica_sql_running, replica_io_running, replication_sql_thread_state, replication_io_thread_state, has_replication_filters, supports_oracle_gtid, oracle_gtid, source_uuid, ancestry_uuid, executed_gtid_set, gtid_mode, gtid_purged, gtid_errant, mariadb_gtid, pseudo_gtid, source_log_file, read_source_log_pos, relay_source_log_file, exec_source_log_pos, relay_log_file, relay_log_pos, last_sql_error, last_io_error, replication_lag_seconds, replica_lag_seconds, sql_delay, data_center, region, physical_environment, replication_depth, is_co_primary, has_replication_credentials, allow_tls, semi_sync_enforced, semi_sync_primary_enabled, semi_sync_primary_timeout, semi_sync_primary_wait_for_replica_count, semi_sync_replica_enabled, semi_sync_primary_status, semi_sync_primary_clients, semi_sync_replica_status, last_discovery_latency, last_seen) VALUES - (?, ?, ?, datetime('now'), datetime('now'), 1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, datetime('now')) + (?, ?, ?, DATETIME('now'), DATETIME('now'), 1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, DATETIME('now')) ` a1 := `zone1-i710, i710, 3306, 710, , 5.6.7, 5.6, MySQL, false, false, STATEMENT, FULL, false, false, , 0, , 0, 0, 0, @@ -88,9 +89,9 @@ func TestMkInsertThree(t *testing.T) { replica_sql_running, replica_io_running, replication_sql_thread_state, replication_io_thread_state, has_replication_filters, supports_oracle_gtid, oracle_gtid, source_uuid, ancestry_uuid, executed_gtid_set, gtid_mode, gtid_purged, gtid_errant, mariadb_gtid, pseudo_gtid, source_log_file, read_source_log_pos, relay_source_log_file, exec_source_log_pos, relay_log_file, relay_log_pos, last_sql_error, last_io_error, replication_lag_seconds, replica_lag_seconds, sql_delay, data_center, region, physical_environment, replication_depth, is_co_primary, has_replication_credentials, allow_tls, semi_sync_enforced, semi_sync_primary_enabled, semi_sync_primary_timeout, semi_sync_primary_wait_for_replica_count, semi_sync_replica_enabled, semi_sync_primary_status, semi_sync_primary_clients, semi_sync_replica_status, last_discovery_latency, last_seen) VALUES - (?, ?, ?, datetime('now'), datetime('now'), 1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, datetime('now')), - (?, ?, ?, datetime('now'), datetime('now'), 1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, datetime('now')), - (?, ?, ?, datetime('now'), datetime('now'), 1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, datetime('now')) + (?, ?, ?, DATETIME('now'), DATETIME('now'), 1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, DATETIME('now')), + (?, ?, ?, DATETIME('now'), DATETIME('now'), 1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, DATETIME('now')), + (?, ?, ?, DATETIME('now'), DATETIME('now'), 1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, DATETIME('now')) ` a3 := ` zone1-i710, i710, 3306, 710, , 5.6.7, 5.6, MySQL, false, false, STATEMENT, FULL, false, false, , 0, , 0, 0, 0, false, false, 0, 0, false, false, false, , , , , , , false, false, , 0, mysql.000007, 10, , 0, , , {0 false}, {0 false}, 0, , , , 0, false, false, false, false, false, 0, 0, false, false, 0, false, 0, @@ -241,11 +242,11 @@ func TestReadProblemInstances(t *testing.T) { // We need to set InstancePollSeconds to a large value otherwise all the instances are reported as having problems since their last_checked is very old. // Setting this value to a hundred years, we ensure that this test doesn't fail with this issue for the next hundred years. - oldVal := config.Config.InstancePollSeconds + oldVal := config.GetInstancePollTime() defer func() { - config.Config.InstancePollSeconds = oldVal + config.SetInstancePollTime(oldVal) }() - config.Config.InstancePollSeconds = 60 * 60 * 24 * 365 * 100 + config.SetInstancePollTime(60 * 60 * 24 * 365 * 100 * time.Second) for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -325,11 +326,11 @@ func TestReadInstancesWithErrantGTIds(t *testing.T) { // We need to set InstancePollSeconds to a large value otherwise all the instances are reported as having problems since their last_checked is very old. // Setting this value to a hundred years, we ensure that this test doesn't fail with this issue for the next hundred years. - oldVal := config.Config.InstancePollSeconds + oldVal := config.GetInstancePollTime() defer func() { - config.Config.InstancePollSeconds = oldVal + config.SetInstancePollTime(oldVal) }() - config.Config.InstancePollSeconds = 60 * 60 * 24 * 365 * 100 + config.SetInstancePollTime(60 * 60 * 24 * 365 * 100 * time.Second) for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -428,27 +429,27 @@ func TestReadOutdatedInstanceKeys(t *testing.T) { }{ { name: "No problems", - sql: []string{"update database_instance set last_checked = datetime('now')"}, + sql: []string{"update database_instance set last_checked = DATETIME('now')"}, instancesRequired: nil, }, { name: "One instance is outdated", sql: []string{ - "update database_instance set last_checked = datetime('now')", - "update database_instance set last_checked = datetime('now', '-1 hour') where alias = 'zone1-0000000100'", + "update database_instance set last_checked = DATETIME('now')", + "update database_instance set last_checked = DATETIME('now', '-1 hour') where alias = 'zone1-0000000100'", }, instancesRequired: []string{"zone1-0000000100"}, }, { name: "One instance doesn't have myql data", sql: []string{ - "update database_instance set last_checked = datetime('now')", + "update database_instance set last_checked = DATETIME('now')", `INSERT INTO vitess_tablet VALUES('zone1-0000000103','localhost',7706,'ks','0','zone1',2,'0001-01-01 00:00:00+00:00','');`, }, instancesRequired: []string{"zone1-0000000103"}, }, { name: "One instance doesn't have myql data and one is outdated", sql: []string{ - "update database_instance set last_checked = datetime('now')", - "update database_instance set last_checked = datetime('now', '-1 hour') where alias = 'zone1-0000000100'", + "update database_instance set last_checked = DATETIME('now')", + "update database_instance set last_checked = DATETIME('now', '-1 hour') where alias = 'zone1-0000000100'", `INSERT INTO vitess_tablet VALUES('zone1-0000000103','localhost',7706,'ks','0','zone1',2,'0001-01-01 00:00:00+00:00','');`, }, instancesRequired: []string{"zone1-0000000103", "zone1-0000000100"}, @@ -459,13 +460,13 @@ func TestReadOutdatedInstanceKeys(t *testing.T) { waitForCacheInitialization() // We are setting InstancePollSeconds to 59 minutes, just for the test. - oldVal := config.Config.InstancePollSeconds + oldVal := config.GetInstancePollTime() oldCache := forgetAliases defer func() { forgetAliases = oldCache - config.Config.InstancePollSeconds = oldVal + config.SetInstancePollTime(oldVal) }() - config.Config.InstancePollSeconds = 60 * 25 + config.SetInstancePollTime(60 * 25 * time.Second) forgetAliases = cache.New(time.Minute, time.Minute) for _, tt := range tests { @@ -485,10 +486,10 @@ func TestReadOutdatedInstanceKeys(t *testing.T) { errInDataCollection := db.QueryVTOrcRowsMap(`select alias, last_checked, last_attempted_check, -ROUND((JULIANDAY(datetime('now')) - JULIANDAY(last_checked)) * 86400) AS difference, +ROUND((JULIANDAY(DATETIME('now')) - JULIANDAY(last_checked)) * 86400) AS difference, last_attempted_check <= last_checked as use1, -last_checked < datetime('now', '-1500 second') as is_outdated1, -last_checked < datetime('now', '-3000 second') as is_outdated2 +last_checked < DATETIME('now', '-1500 second') as is_outdated1, +last_checked < DATETIME('now', '-3000 second') as is_outdated2 from database_instance`, func(rowMap sqlutils.RowMap) error { log.Errorf("Row in database_instance - %+v", rowMap) return nil @@ -512,12 +513,12 @@ func TestUpdateInstanceLastChecked(t *testing.T) { name: "Verify updated last checked", tabletAlias: "zone1-0000000100", partialSuccess: false, - conditionToCheck: "last_checked >= datetime('now', '-30 second') and last_check_partial_success = false", + conditionToCheck: "last_checked >= DATETIME('now', '-30 second') and last_check_partial_success = false", }, { name: "Verify partial success", tabletAlias: "zone1-0000000100", partialSuccess: true, - conditionToCheck: "last_checked >= datetime('now', '-30 second') and last_check_partial_success = true", + conditionToCheck: "last_checked >= DATETIME('now', '-30 second') and last_check_partial_success = true", }, { name: "Verify no error on unknown tablet", tabletAlias: "unknown tablet", @@ -563,7 +564,7 @@ func TestUpdateInstanceLastAttemptedCheck(t *testing.T) { { name: "Verify updated last checked", tabletAlias: "zone1-0000000100", - conditionToCheck: "last_attempted_check >= datetime('now', '-30 second')", + conditionToCheck: "last_attempted_check >= DATETIME('now', '-30 second')", }, { name: "Verify no error on unknown tablet", tabletAlias: "unknown tablet", @@ -718,10 +719,10 @@ func TestGetDatabaseState(t *testing.T) { } func TestExpireTableData(t *testing.T) { - oldVal := config.Config.AuditPurgeDays - config.Config.AuditPurgeDays = 10 + oldVal := config.GetAuditPurgeDays() + config.SetAuditPurgeDays(10) defer func() { - config.Config.AuditPurgeDays = oldVal + config.SetAuditPurgeDays(oldVal) }() tests := []struct { @@ -736,19 +737,19 @@ func TestExpireTableData(t *testing.T) { tableName: "audit", timestampColumn: "audit_timestamp", expectedRowCount: 1, - insertQuery: `insert into audit (audit_id, audit_timestamp, audit_type, alias, message, keyspace, shard) values -(1, datetime('now', '-50 DAY'), 'a','a','a','a','a'), -(2, datetime('now', '-5 DAY'), 'a','a','a','a','a')`, + insertQuery: `INSERT INTO audit (audit_id, audit_timestamp, audit_type, alias, message, keyspace, shard) VALUES +(1, DATETIME('now', '-50 DAY'), 'a','a','a','a','a'), +(2, DATETIME('now', '-5 DAY'), 'a','a','a','a','a')`, }, { name: "ExpireRecoveryDetectionHistory", tableName: "recovery_detection", timestampColumn: "detection_timestamp", expectedRowCount: 2, - insertQuery: `insert into recovery_detection (detection_id, detection_timestamp, alias, analysis, keyspace, shard) values -(1, datetime('now', '-3 DAY'),'a','a','a','a'), -(2, datetime('now', '-5 DAY'),'a','a','a','a'), -(3, datetime('now', '-15 DAY'),'a','a','a','a')`, + insertQuery: `INSERT INTO recovery_detection (detection_id, detection_timestamp, alias, analysis, keyspace, shard) VALUES +(1, DATETIME('now', '-3 DAY'),'a','a','a','a'), +(2, DATETIME('now', '-5 DAY'),'a','a','a','a'), +(3, DATETIME('now', '-15 DAY'),'a','a','a','a')`, }, } for _, tt := range tests { @@ -773,3 +774,165 @@ func TestExpireTableData(t *testing.T) { }) } } + +func TestDetectErrantGTIDs(t *testing.T) { + tests := []struct { + name string + instance *Instance + primaryInstance *Instance + wantErr bool + wantErrantGTID string + }{ + { + name: "No errant GTIDs", + instance: &Instance{ + ExecutedGtidSet: "230ea8ea-81e3-11e4-972a-e25ec4bd140a:1-10539,8bc65c84-3fe4-11ed-a912-257f0fcdd6c9:1-34", + primaryExecutedGtidSet: "230ea8ea-81e3-11e4-972a-e25ec4bd140a:1-10591,8bc65c84-3fe4-11ed-a912-257f0fcdd6c9:1-34", + AncestryUUID: "316d193c-70e5-11e5-adb2-ecf4bb2262ff,230ea8ea-81e3-11e4-972a-e25ec4bd140a", + ServerUUID: "316d193c-70e5-11e5-adb2-ecf4bb2262ff", + SourceUUID: "230ea8ea-81e3-11e4-972a-e25ec4bd140a", + }, + }, { + name: "Errant GTIDs on replica", + instance: &Instance{ + ExecutedGtidSet: "230ea8ea-81e3-11e4-972a-e25ec4bd140a:1-10539,8bc65c84-3fe4-11ed-a912-257f0fcdd6c9:1-34,316d193c-70e5-11e5-adb2-ecf4bb2262ff:34", + primaryExecutedGtidSet: "230ea8ea-81e3-11e4-972a-e25ec4bd140a:1-10591,8bc65c84-3fe4-11ed-a912-257f0fcdd6c9:1-34", + AncestryUUID: "316d193c-70e5-11e5-adb2-ecf4bb2262ff,230ea8ea-81e3-11e4-972a-e25ec4bd140a", + ServerUUID: "316d193c-70e5-11e5-adb2-ecf4bb2262ff", + SourceUUID: "230ea8ea-81e3-11e4-972a-e25ec4bd140a", + }, + wantErrantGTID: "316d193c-70e5-11e5-adb2-ecf4bb2262ff:34", + }, + { + name: "No errant GTIDs on old primary", + instance: &Instance{ + ExecutedGtidSet: "230ea8ea-81e3-11e4-972a-e25ec4bd140a:1-10539,8bc65c84-3fe4-11ed-a912-257f0fcdd6c9:1-34,316d193c-70e5-11e5-adb2-ecf4bb2262ff:1-341", + AncestryUUID: "316d193c-70e5-11e5-adb2-ecf4bb2262ff", + ServerUUID: "316d193c-70e5-11e5-adb2-ecf4bb2262ff", + }, + primaryInstance: &Instance{ + SourceHost: "", + ExecutedGtidSet: "230ea8ea-81e3-11e4-972a-e25ec4bd140a:1-10589,8bc65c84-3fe4-11ed-a912-257f0fcdd6c9:1-34,316d193c-70e5-11e5-adb2-ecf4bb2262ff:1-341", + }, + }, + { + name: "Errant GTIDs on old primary", + instance: &Instance{ + ExecutedGtidSet: "230ea8ea-81e3-11e4-972a-e25ec4bd140a:1-10539,8bc65c84-3fe4-11ed-a912-257f0fcdd6c9:1-34,316d193c-70e5-11e5-adb2-ecf4bb2262ff:1-342", + AncestryUUID: "316d193c-70e5-11e5-adb2-ecf4bb2262ff", + ServerUUID: "316d193c-70e5-11e5-adb2-ecf4bb2262ff", + }, + primaryInstance: &Instance{ + SourceHost: "", + ExecutedGtidSet: "230ea8ea-81e3-11e4-972a-e25ec4bd140a:1-10589,8bc65c84-3fe4-11ed-a912-257f0fcdd6c9:1-34,316d193c-70e5-11e5-adb2-ecf4bb2262ff:1-341", + }, + wantErrantGTID: "316d193c-70e5-11e5-adb2-ecf4bb2262ff:342", + }, { + name: "Old information for new primary", + instance: &Instance{ + ExecutedGtidSet: "230ea8ea-81e3-11e4-972a-e25ec4bd140a:1-10539,8bc65c84-3fe4-11ed-a912-257f0fcdd6c9:1-34,316d193c-70e5-11e5-adb2-ecf4bb2262ff:1-342", + AncestryUUID: "316d193c-70e5-11e5-adb2-ecf4bb2262ff", + ServerUUID: "316d193c-70e5-11e5-adb2-ecf4bb2262ff", + }, + primaryInstance: &Instance{ + SourceHost: "localhost", + ExecutedGtidSet: "230ea8ea-81e3-11e4-972a-e25ec4bd140a:1-10539,8bc65c84-3fe4-11ed-a912-257f0fcdd6c9:1-34,316d193c-70e5-11e5-adb2-ecf4bb2262ff:1-311", + }, + }, + } + + keyspaceName := "ks" + shardName := "0" + tablet := &topodatapb.Tablet{ + Alias: &topodatapb.TabletAlias{ + Cell: "zone-1", + Uid: 100, + }, + Keyspace: keyspaceName, + Shard: shardName, + } + primaryTablet := &topodatapb.Tablet{ + Alias: &topodatapb.TabletAlias{ + Cell: "zone-1", + Uid: 101, + }, + Keyspace: keyspaceName, + Shard: shardName, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Clear the database after the test. The easiest way to do that is to run all the initialization commands again. + defer func() { + db.ClearVTOrcDatabase() + }() + db.ClearVTOrcDatabase() + + // Save shard record for the primary tablet. + err := SaveShard(topo.NewShardInfo(keyspaceName, shardName, &topodatapb.Shard{ + PrimaryAlias: primaryTablet.Alias, + }, nil)) + require.NoError(t, err) + + if tt.primaryInstance != nil { + tt.primaryInstance.InstanceAlias = topoproto.TabletAliasString(primaryTablet.Alias) + err = SaveTablet(primaryTablet) + require.NoError(t, err) + err = WriteInstance(tt.primaryInstance, true, nil) + require.NoError(t, err) + } + + tt.instance.InstanceAlias = topoproto.TabletAliasString(tablet.Alias) + err = detectErrantGTIDs(tt.instance, tablet) + if tt.wantErr { + require.Error(t, err) + return + } + require.NoError(t, err) + require.EqualValues(t, tt.wantErrantGTID, tt.instance.GtidErrant) + }) + } +} + +// TestPrimaryErrantGTIDs tests that we don't run Errant GTID detection on the primary tablet itself! +func TestPrimaryErrantGTIDs(t *testing.T) { + // Clear the database after the test. The easiest way to do that is to run all the initialization commands again. + defer func() { + db.ClearVTOrcDatabase() + }() + db.ClearVTOrcDatabase() + keyspaceName := "ks" + shardName := "0" + tablet := &topodatapb.Tablet{ + Alias: &topodatapb.TabletAlias{ + Cell: "zone-1", + Uid: 100, + }, + Keyspace: keyspaceName, + Shard: shardName, + } + instance := &Instance{ + SourceHost: "", + ExecutedGtidSet: "230ea8ea-81e3-11e4-972a-e25ec4bd140a:1-10589,8bc65c84-3fe4-11ed-a912-257f0fcdd6c9:1-34,316d193c-70e5-11e5-adb2-ecf4bb2262ff:1-341", + InstanceAlias: topoproto.TabletAliasString(tablet.Alias), + } + + // Save shard record for the primary tablet. + err := SaveShard(topo.NewShardInfo(keyspaceName, shardName, &topodatapb.Shard{ + PrimaryAlias: tablet.Alias, + }, nil)) + require.NoError(t, err) + + // Store the tablet record and the instance. + err = SaveTablet(tablet) + require.NoError(t, err) + err = WriteInstance(instance, true, nil) + require.NoError(t, err) + + // After this if we read a new information for the record that updates its + // gtid set further, we shouldn't be detecting errant GTIDs on it since it is the primary! + // We shouldn't be comparing it with a previous version of itself! + instance.ExecutedGtidSet = "230ea8ea-81e3-11e4-972a-e25ec4bd140a:1-10589,8bc65c84-3fe4-11ed-a912-257f0fcdd6c9:1-34,316d193c-70e5-11e5-adb2-ecf4bb2262ff:1-351" + err = detectErrantGTIDs(instance, tablet) + require.NoError(t, err) + require.EqualValues(t, "", instance.GtidErrant) +} diff --git a/go/vt/vtorc/inst/tablet_dao.go b/go/vt/vtorc/inst/tablet_dao.go index af304292a70..f48f2b97370 100644 --- a/go/vt/vtorc/inst/tablet_dao.go +++ b/go/vt/vtorc/inst/tablet_dao.go @@ -56,13 +56,13 @@ func fullStatus(tabletAlias string) (*replicationdatapb.FullStatus, error) { // ReadTablet reads the vitess tablet record. func ReadTablet(tabletAlias string) (*topodatapb.Tablet, error) { - query := ` - select - info - from - vitess_tablet - where alias = ? - ` + query := `SELECT + info + FROM + vitess_tablet + WHERE + alias = ? + ` args := sqlutils.Args(tabletAlias) tablet := &topodatapb.Tablet{} opts := prototext.UnmarshalOptions{DiscardUnknown: true} @@ -84,14 +84,28 @@ func SaveTablet(tablet *topodatapb.Tablet) error { if err != nil { return err } - _, err = db.ExecVTOrc(` - replace - into vitess_tablet ( - alias, hostname, port, cell, keyspace, shard, tablet_type, primary_timestamp, info - ) values ( - ?, ?, ?, ?, ?, ?, ?, ?, ? - ) - `, + _, err = db.ExecVTOrc(`REPLACE + INTO vitess_tablet ( + alias, + hostname, + port, + cell, + keyspace, + shard, + tablet_type, + primary_timestamp, + info + ) VALUES ( + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ? + )`, topoproto.TabletAliasString(tablet.Alias), tablet.MysqlHostname, int(tablet.MysqlPort), diff --git a/go/vt/vtorc/logic/disable_recovery.go b/go/vt/vtorc/logic/disable_recovery.go index 60650798876..c5446eeb9ff 100644 --- a/go/vt/vtorc/logic/disable_recovery.go +++ b/go/vt/vtorc/logic/disable_recovery.go @@ -40,14 +40,13 @@ import ( // IsRecoveryDisabled returns true if Recoveries are disabled globally func IsRecoveryDisabled() (disabled bool, err error) { - query := ` - SELECT - COUNT(*) as mycount - FROM - global_recovery_disable - WHERE - disable_recovery=? - ` + query := `SELECT + COUNT(*) AS mycount + FROM + global_recovery_disable + WHERE + disable_recovery = ? + ` err = db.QueryVTOrc(query, sqlutils.Args(1), func(m sqlutils.RowMap) error { mycount := m.GetInt("mycount") disabled = (mycount > 0) @@ -63,21 +62,19 @@ func IsRecoveryDisabled() (disabled bool, err error) { // DisableRecovery ensures recoveries are disabled globally func DisableRecovery() error { - _, err := db.ExecVTOrc(` - INSERT OR IGNORE INTO global_recovery_disable - (disable_recovery) - VALUES (1) - `, - ) + _, err := db.ExecVTOrc(`INSERT OR IGNORE + INTO global_recovery_disable ( + disable_recovery + ) VALUES (1)`) return err } // EnableRecovery ensures recoveries are enabled globally func EnableRecovery() error { // The "WHERE" clause is just to avoid full-scan reports by monitoring tools - _, err := db.ExecVTOrc(` - DELETE FROM global_recovery_disable WHERE disable_recovery >= 0 - `, - ) + _, err := db.ExecVTOrc(`DELETE + FROM global_recovery_disable + WHERE + disable_recovery >= 0`) return err } diff --git a/go/vt/vtorc/logic/tablet_discovery.go b/go/vt/vtorc/logic/tablet_discovery.go index e9bbcee35cb..e62c0652e62 100644 --- a/go/vt/vtorc/logic/tablet_discovery.go +++ b/go/vt/vtorc/logic/tablet_discovery.go @@ -66,12 +66,12 @@ func OpenTabletDiscovery() <-chan time.Time { ts = topo.Open() tmc = inst.InitializeTMC() // Clear existing cache and perform a new refresh. - if _, err := db.ExecVTOrc("delete from vitess_tablet"); err != nil { + if _, err := db.ExecVTOrc("DELETE FROM vitess_tablet"); err != nil { log.Error(err) } // We refresh all information from the topo once before we start the ticks to do it on a timer. populateAllInformation() - return time.Tick(time.Second * time.Duration(config.Config.TopoInformationRefreshSeconds)) //nolint SA1015: using time.Tick leaks the underlying ticker + return time.Tick(config.GetTopoInformationRefreshDuration()) //nolint SA1015: using time.Tick leaks the underlying ticker } // populateAllInformation initializes all the information for VTOrc to function. diff --git a/go/vt/vtorc/logic/topology_recovery.go b/go/vt/vtorc/logic/topology_recovery.go index aec137a45b4..f14eca624c9 100644 --- a/go/vt/vtorc/logic/topology_recovery.go +++ b/go/vt/vtorc/logic/topology_recovery.go @@ -21,7 +21,6 @@ import ( "encoding/json" "fmt" "math/rand/v2" - "time" "vitess.io/vitess/go/stats" "vitess.io/vitess/go/vt/log" @@ -235,8 +234,8 @@ func runEmergencyReparentOp(ctx context.Context, analysisEntry *inst.Replication tablet.Shard, reparentutil.EmergencyReparentOptions{ IgnoreReplicas: nil, - WaitReplicasTimeout: time.Duration(config.Config.WaitReplicasTimeoutSeconds) * time.Second, - PreventCrossCellPromotion: config.Config.PreventCrossDataCenterPrimaryFailover, + WaitReplicasTimeout: config.GetWaitReplicasTimeout(), + PreventCrossCellPromotion: config.GetPreventCrossCellFailover(), WaitAllTablets: waitForAllTablets, }, ) @@ -703,8 +702,8 @@ func electNewPrimary(ctx context.Context, analysisEntry *inst.ReplicationAnalysi analyzedTablet.Keyspace, analyzedTablet.Shard, reparentutil.PlannedReparentOptions{ - WaitReplicasTimeout: time.Duration(config.Config.WaitReplicasTimeoutSeconds) * time.Second, - TolerableReplLag: time.Duration(config.Config.TolerableReplicationLagSeconds) * time.Second, + WaitReplicasTimeout: config.GetWaitReplicasTimeout(), + TolerableReplLag: config.GetTolerableReplicationLag(), }, ) diff --git a/go/vt/vtorc/logic/topology_recovery_dao.go b/go/vt/vtorc/logic/topology_recovery_dao.go index 730e6b2a158..137251c4fc8 100644 --- a/go/vt/vtorc/logic/topology_recovery_dao.go +++ b/go/vt/vtorc/logic/topology_recovery_dao.go @@ -30,21 +30,20 @@ import ( // InsertRecoveryDetection inserts the recovery analysis that has been detected. func InsertRecoveryDetection(analysisEntry *inst.ReplicationAnalysis) error { - sqlResult, err := db.ExecVTOrc(` - insert or ignore - into recovery_detection ( - alias, - analysis, - keyspace, - shard, - detection_timestamp - ) values ( - ?, - ?, - ?, - ?, - datetime('now') - )`, + sqlResult, err := db.ExecVTOrc(`INSERT OR IGNORE + INTO recovery_detection ( + alias, + analysis, + keyspace, + shard, + detection_timestamp + ) VALUES ( + ?, + ?, + ?, + ?, + DATETIME('now') + )`, analysisEntry.AnalyzedInstanceAlias, string(analysisEntry.Analysis), analysisEntry.ClusterDetails.Keyspace, @@ -65,26 +64,24 @@ func InsertRecoveryDetection(analysisEntry *inst.ReplicationAnalysis) error { func writeTopologyRecovery(topologyRecovery *TopologyRecovery) (*TopologyRecovery, error) { analysisEntry := topologyRecovery.AnalysisEntry - sqlResult, err := db.ExecVTOrc(` - insert or ignore - into topology_recovery ( - recovery_id, - alias, - start_recovery, - analysis, - keyspace, - shard, - detection_id - ) values ( - ?, - ?, - datetime('now'), - ?, - ?, - ?, - ? - ) - `, + sqlResult, err := db.ExecVTOrc(`INSERT OR IGNORE + INTO topology_recovery ( + recovery_id, + alias, + start_recovery, + analysis, + keyspace, + shard, + detection_id + ) VALUES ( + ?, + ?, + DATETIME('now'), + ?, + ?, + ?, + ? + )`, sqlutils.NilIfZero(topologyRecovery.ID), analysisEntry.AnalyzedInstanceAlias, string(analysisEntry.Analysis), @@ -138,15 +135,16 @@ func AttemptRecoveryRegistration(analysisEntry *inst.ReplicationAnalysis) (*Topo // ResolveRecovery is called on completion of a recovery process and updates the recovery status. // It does not clear the "active period" as this still takes place in order to avoid flapping. func writeResolveRecovery(topologyRecovery *TopologyRecovery) error { - _, err := db.ExecVTOrc(` - update topology_recovery set - is_successful = ?, - successor_alias = ?, - all_errors = ?, - end_recovery = datetime('now') - where - recovery_id = ? - `, topologyRecovery.IsSuccessful, + _, err := db.ExecVTOrc(`UPDATE topology_recovery + SET + is_successful = ?, + successor_alias = ?, + all_errors = ?, + end_recovery = DATETIME('now') + WHERE + recovery_id = ? + `, + topologyRecovery.IsSuccessful, topologyRecovery.SuccessorAlias, strings.Join(topologyRecovery.AllErrors, "\n"), topologyRecovery.ID, @@ -160,26 +158,27 @@ func writeResolveRecovery(topologyRecovery *TopologyRecovery) error { // readRecoveries reads recovery entry/audit entries from topology_recovery func readRecoveries(whereCondition string, limit string, args []any) ([]*TopologyRecovery, error) { res := []*TopologyRecovery{} - query := fmt.Sprintf(` - select - recovery_id, - alias, - start_recovery, - IFNULL(end_recovery, '') AS end_recovery, - is_successful, - ifnull(successor_alias, '') as successor_alias, - analysis, - keyspace, - shard, - all_errors, - detection_id - from + query := fmt.Sprintf(`SELECT + recovery_id, + alias, + start_recovery, + IFNULL(end_recovery, '') AS end_recovery, + is_successful, + IFNULL(successor_alias, '') AS successor_alias, + analysis, + keyspace, + shard, + all_errors, + detection_id + FROM topology_recovery %s - order by - recovery_id desc + ORDER BY recovery_id DESC %s - `, whereCondition, limit) + `, + whereCondition, + limit, + ) err := db.QueryVTOrc(query, args, func(m sqlutils.RowMap) error { topologyRecovery := *NewTopologyRecovery(inst.ReplicationAnalysis{}) topologyRecovery.ID = m.GetInt64("recovery_id") @@ -211,11 +210,10 @@ func readRecoveries(whereCondition string, limit string, args []any) ([]*Topolog // ReadActiveClusterRecoveries reads recoveries that are ongoing for the given cluster. func ReadActiveClusterRecoveries(keyspace string, shard string) ([]*TopologyRecovery, error) { - whereClause := ` - where - end_recovery IS NULL - and keyspace=? - and shard=?` + whereClause := `WHERE + end_recovery IS NULL + AND keyspace = ? + AND shard = ?` return readRecoveries(whereClause, ``, sqlutils.Args(keyspace, shard)) } @@ -225,23 +223,30 @@ func ReadRecentRecoveries(page int) ([]*TopologyRecovery, error) { whereClause := "" var args []any if len(whereConditions) > 0 { - whereClause = fmt.Sprintf("where %s", strings.Join(whereConditions, " and ")) + whereClause = fmt.Sprintf("WHERE %s", strings.Join(whereConditions, " AND ")) } - limit := ` - limit ? - offset ?` + limit := `LIMIT ? OFFSET ?` args = append(args, config.AuditPageSize, page*config.AuditPageSize) return readRecoveries(whereClause, limit, args) } // writeTopologyRecoveryStep writes down a single step in a recovery process func writeTopologyRecoveryStep(topologyRecoveryStep *TopologyRecoveryStep) error { - sqlResult, err := db.ExecVTOrc(` - insert or ignore - into topology_recovery_steps ( - recovery_step_id, recovery_id, audit_at, message - ) values (?, ?, datetime('now'), ?) - `, sqlutils.NilIfZero(topologyRecoveryStep.ID), topologyRecoveryStep.RecoveryID, topologyRecoveryStep.Message, + sqlResult, err := db.ExecVTOrc(`INSERT OR IGNORE + INTO topology_recovery_steps ( + recovery_step_id, + recovery_id, + audit_at, + message + ) VALUES ( + ?, + ?, + DATETIME('now'), + ? + )`, + sqlutils.NilIfZero(topologyRecoveryStep.ID), + topologyRecoveryStep.RecoveryID, + topologyRecoveryStep.Message, ) if err != nil { log.Error(err) diff --git a/go/vt/vtorc/logic/topology_recovery_dao_test.go b/go/vt/vtorc/logic/topology_recovery_dao_test.go index 20dfb7e91e2..6a1d7c4c48f 100644 --- a/go/vt/vtorc/logic/topology_recovery_dao_test.go +++ b/go/vt/vtorc/logic/topology_recovery_dao_test.go @@ -70,10 +70,10 @@ func TestTopologyRecovery(t *testing.T) { } func TestExpireTableData(t *testing.T) { - oldVal := config.Config.AuditPurgeDays - config.Config.AuditPurgeDays = 10 + oldVal := config.GetAuditPurgeDays() + config.SetAuditPurgeDays(10) defer func() { - config.Config.AuditPurgeDays = oldVal + config.SetAuditPurgeDays(oldVal) }() tests := []struct { diff --git a/go/vt/vtorc/logic/vtorc.go b/go/vt/vtorc/logic/vtorc.go index 9a468d1508a..b8cf404d050 100644 --- a/go/vt/vtorc/logic/vtorc.go +++ b/go/vt/vtorc/logic/vtorc.go @@ -17,11 +17,8 @@ package logic import ( - "os" - "os/signal" "sync" "sync/atomic" - "syscall" "time" "github.com/patrickmn/go-cache" @@ -73,26 +70,6 @@ func init() { }) } -// used in several places -func instancePollSecondsDuration() time.Duration { - return time.Duration(config.Config.InstancePollSeconds) * time.Second -} - -// acceptSighupSignal registers for SIGHUP signal from the OS to reload the configuration files. -func acceptSighupSignal() { - c := make(chan os.Signal, 1) - - signal.Notify(c, syscall.SIGHUP) - go func() { - for range c { - log.Infof("Received SIGHUP. Reloading configuration") - _ = inst.AuditOperation("reload-configuration", "", "Triggered via SIGHUP") - config.Reload() - discoveryMetrics.SetExpirePeriod(time.Duration(config.DiscoveryCollectionRetentionSeconds) * time.Second) - } - }() -} - // closeVTOrc runs all the operations required to cleanly shutdown VTOrc func closeVTOrc() { log.Infof("Starting VTOrc shutdown") @@ -161,7 +138,7 @@ func DiscoverInstance(tabletAlias string, forceDiscovery bool) { defer func() { latency.Stop("total") discoveryTime := latency.Elapsed("total") - if discoveryTime > instancePollSecondsDuration() { + if discoveryTime > config.GetInstancePollTime() { instancePollSecondsExceededCounter.Add(1) log.Warningf("discoverInstance exceeded InstancePollSeconds for %+v, took %.4fs", tabletAlias, discoveryTime.Seconds()) if metric != nil { @@ -177,7 +154,7 @@ func DiscoverInstance(tabletAlias string, forceDiscovery bool) { // Calculate the expiry period each time as InstancePollSeconds // _may_ change during the run of the process (via SIGHUP) and // it is not possible to change the cache's default expiry.. - if existsInCacheError := recentDiscoveryOperationKeys.Add(tabletAlias, true, instancePollSecondsDuration()); existsInCacheError != nil && !forceDiscovery { + if existsInCacheError := recentDiscoveryOperationKeys.Add(tabletAlias, true, config.GetInstancePollTime()); existsInCacheError != nil && !forceDiscovery { // Just recently attempted return } @@ -271,24 +248,23 @@ func onHealthTick() { // nolint SA1015: using time.Tick leaks the underlying ticker func ContinuousDiscovery() { log.Infof("continuous discovery: setting up") - recentDiscoveryOperationKeys = cache.New(instancePollSecondsDuration(), time.Second) + recentDiscoveryOperationKeys = cache.New(config.GetInstancePollTime(), time.Second) go handleDiscoveryRequests() healthTick := time.Tick(config.HealthPollSeconds * time.Second) caretakingTick := time.Tick(time.Minute) - recoveryTick := time.Tick(time.Duration(config.Config.RecoveryPollSeconds) * time.Second) + recoveryTick := time.Tick(config.GetRecoveryPollDuration()) tabletTopoTick := OpenTabletDiscovery() var recoveryEntrance int64 var snapshotTopologiesTick <-chan time.Time - if config.Config.SnapshotTopologiesIntervalHours > 0 { - snapshotTopologiesTick = time.Tick(time.Duration(config.Config.SnapshotTopologiesIntervalHours) * time.Hour) + if config.GetSnapshotTopologyInterval() > 0 { + snapshotTopologiesTick = time.Tick(config.GetSnapshotTopologyInterval()) } go func() { _ = ometrics.InitMetrics() }() - go acceptSighupSignal() // On termination of the server, we should close VTOrc cleanly servenv.OnTermSync(closeVTOrc) diff --git a/go/vt/vtorc/process/health.go b/go/vt/vtorc/process/health.go index 87a11733f66..f72d7b05210 100644 --- a/go/vt/vtorc/process/health.go +++ b/go/vt/vtorc/process/health.go @@ -35,12 +35,17 @@ var ThisNodeHealth = &NodeHealth{} // writeHealthToDatabase writes to the database and returns if it was successful. func writeHealthToDatabase() bool { - _, err := db.ExecVTOrc("delete from node_health") + _, err := db.ExecVTOrc("DELETE FROM node_health") if err != nil { log.Error(err) return false } - sqlResult, err := db.ExecVTOrc(`insert into node_health (last_seen_active) values (datetime('now'))`) + sqlResult, err := db.ExecVTOrc(`INSERT + INTO node_health ( + last_seen_active + ) VALUES ( + DATETIME('now') + )`) if err != nil { log.Error(err) return false diff --git a/go/vt/vtorc/server/api.go b/go/vt/vtorc/server/api.go index 5e9a84c0a29..177f2c80333 100644 --- a/go/vt/vtorc/server/api.go +++ b/go/vt/vtorc/server/api.go @@ -25,6 +25,7 @@ import ( "time" "vitess.io/vitess/go/acl" + "vitess.io/vitess/go/viperutil/debug" "vitess.io/vitess/go/vt/servenv" "vitess.io/vitess/go/vt/vtorc/collection" "vitess.io/vitess/go/vt/vtorc/discovery" @@ -46,6 +47,7 @@ const ( enableGlobalRecoveriesAPI = "/api/enable-global-recoveries" replicationAnalysisAPI = "/api/replication-analysis" databaseStateAPI = "/api/database-state" + configAPI = "/api/config" healthAPI = "/debug/health" AggregatedDiscoveryMetricsAPI = "/api/aggregated-discovery-metrics" @@ -62,6 +64,7 @@ var ( enableGlobalRecoveriesAPI, replicationAnalysisAPI, databaseStateAPI, + configAPI, healthAPI, AggregatedDiscoveryMetricsAPI, } @@ -90,6 +93,8 @@ func (v *vtorcAPI) ServeHTTP(response http.ResponseWriter, request *http.Request replicationAnalysisAPIHandler(response, request) case databaseStateAPI: databaseStateAPIHandler(response) + case configAPI: + configAPIHandler(response) case AggregatedDiscoveryMetricsAPI: AggregatedDiscoveryMetricsAPIHandler(response, request) default: @@ -106,7 +111,7 @@ func getACLPermissionLevelForAPI(apiEndpoint string) string { return acl.MONITORING case disableGlobalRecoveriesAPI, enableGlobalRecoveriesAPI: return acl.ADMIN - case replicationAnalysisAPI: + case replicationAnalysisAPI, configAPI: return acl.MONITORING case healthAPI, databaseStateAPI: return acl.MONITORING @@ -180,6 +185,17 @@ func databaseStateAPIHandler(response http.ResponseWriter) { writePlainTextResponse(response, ds, http.StatusOK) } +// configAPIHandler is the handler for the configAPI endpoint +func configAPIHandler(response http.ResponseWriter) { + settingsMap := debug.AllSettings() + jsonOut, err := json.MarshalIndent(settingsMap, "", "\t") + if err != nil { + http.Error(response, err.Error(), http.StatusInternalServerError) + return + } + writePlainTextResponse(response, string(jsonOut), http.StatusOK) +} + // AggregatedDiscoveryMetricsAPIHandler is the handler for the discovery metrics endpoint func AggregatedDiscoveryMetricsAPIHandler(response http.ResponseWriter, request *http.Request) { // return metrics for last x seconds diff --git a/go/vt/vtorc/server/api_test.go b/go/vt/vtorc/server/api_test.go index c352d1e600f..ab6b9eed9af 100644 --- a/go/vt/vtorc/server/api_test.go +++ b/go/vt/vtorc/server/api_test.go @@ -31,6 +31,9 @@ func TestGetACLPermissionLevelForAPI(t *testing.T) { }, { apiEndpoint: healthAPI, want: acl.MONITORING, + }, { + apiEndpoint: configAPI, + want: acl.MONITORING, }, { apiEndpoint: "gibberish", want: acl.ADMIN, diff --git a/go/vt/vttablet/common/flags.go b/go/vt/vttablet/common/flags.go index 3c6141d62eb..75e8e58982f 100644 --- a/go/vt/vttablet/common/flags.go +++ b/go/vt/vttablet/common/flags.go @@ -33,8 +33,7 @@ const ( ) var ( - // Default flags: currently VReplicationExperimentalFlagVPlayerBatching is not enabled by default. - vreplicationExperimentalFlags = VReplicationExperimentalFlagOptimizeInserts | VReplicationExperimentalFlagAllowNoBlobBinlogRowImage + vreplicationExperimentalFlags = VReplicationExperimentalFlagOptimizeInserts | VReplicationExperimentalFlagAllowNoBlobBinlogRowImage | VReplicationExperimentalFlagVPlayerBatching vreplicationNetReadTimeout = 300 vreplicationNetWriteTimeout = 600 vreplicationCopyPhaseDuration = 1 * time.Hour diff --git a/go/vt/vttablet/endtoend/config_test.go b/go/vt/vttablet/endtoend/config_test.go index 4abf5b36c21..c3ad5f8a9db 100644 --- a/go/vt/vttablet/endtoend/config_test.go +++ b/go/vt/vttablet/endtoend/config_test.go @@ -36,7 +36,7 @@ import ( ) func TestPoolSize(t *testing.T) { - revert := changeVar(t, "PoolSize", "1") + revert := changeVar(t, "ReadPoolSize", "1") defer revert() vstart := framework.DebugVars() @@ -92,7 +92,7 @@ func TestTxPoolSize(t *testing.T) { defer client2.Rollback() verifyIntValue(t, framework.DebugVars(), "FoundRowsPoolAvailable", framework.FetchInt(vstart, "FoundRowsPoolAvailable")-1) - revert := changeVar(t, "TxPoolSize", "1") + revert := changeVar(t, "TransactionPoolSize", "1") defer revert() vend := framework.DebugVars() verifyIntValue(t, vend, "TransactionPoolAvailable", 0) diff --git a/go/vt/vttablet/onlineddl/executor.go b/go/vt/vttablet/onlineddl/executor.go index 002cd1fb6d0..f8b5cfd9b8d 100644 --- a/go/vt/vttablet/onlineddl/executor.go +++ b/go/vt/vttablet/onlineddl/executor.go @@ -3583,53 +3583,36 @@ func (e *Executor) isPreserveForeignKeySupported(ctx context.Context) (isSupport // and is up to date with the binlogs. func (e *Executor) isVReplMigrationReadyToCutOver(ctx context.Context, onlineDDL *schema.OnlineDDL, s *VReplStream) (isReady bool, err error) { // Check all the cases where migration is still running: - { - // when ready to cut-over, pos must have some value - if s.pos == "" { - return false, nil - } + // when ready to cut-over, pos must have some value + if s.pos == "" { + return false, nil } - { - // Both time_updated and transaction_timestamp must be in close proximity to each - // other and to the time now, otherwise that means we're lagging and it's not a good time - // to cut-over - durationDiff := func(t1, t2 time.Time) time.Duration { - return t1.Sub(t2).Abs() - } - timeNow := time.Now() - timeUpdated := time.Unix(s.timeUpdated, 0) - if durationDiff(timeNow, timeUpdated) > onlineDDL.CutOverThreshold { - return false, nil - } - // Let's look at transaction timestamp. This gets written by any ongoing - // writes on the server (whether on this table or any other table) - transactionTimestamp := time.Unix(s.transactionTimestamp, 0) - if durationDiff(timeNow, transactionTimestamp) > onlineDDL.CutOverThreshold { - return false, nil - } + // Both time_updated and transaction_timestamp must be in close proximity to each + // other and to the time now, otherwise that means we're lagging and it's not a good time + // to cut-over + if s.Lag() > onlineDDL.CutOverThreshold { + return false, nil } - { - // copy_state must have no entries for this vreplication id: if entries are - // present that means copy is still in progress - query, err := sqlparser.ParseAndBind(sqlReadCountCopyState, - sqltypes.Int32BindVariable(s.id), - ) - if err != nil { - return false, err - } - r, err := e.execQuery(ctx, query) - if err != nil { - return false, err - } - csRow := r.Named().Row() - if csRow == nil { - return false, err - } - count := csRow.AsInt64("cnt", 0) - if count > 0 { - // Still copying - return false, nil - } + // copy_state must have no entries for this vreplication id: if entries are + // present that means copy is still in progress + query, err := sqlparser.ParseAndBind(sqlReadCountCopyState, + sqltypes.Int32BindVariable(s.id), + ) + if err != nil { + return false, err + } + r, err := e.execQuery(ctx, query) + if err != nil { + return false, err + } + csRow := r.Named().Row() + if csRow == nil { + return false, err + } + count := csRow.AsInt64("cnt", 0) + if count > 0 { + // Still copying + return false, nil } return true, nil @@ -3776,6 +3759,7 @@ func (e *Executor) reviewRunningMigrations(ctx context.Context) (countRunnning i } _ = e.updateRowsCopied(ctx, uuid, s.rowsCopied) _ = e.updateMigrationProgressByRowsCopied(ctx, uuid, s.rowsCopied) + _ = e.updateMigrationVreplicationLagSeconds(ctx, uuid, int64(s.Lag().Seconds())) _ = e.updateMigrationETASecondsByProgress(ctx, uuid) if s.timeThrottled != 0 { // Avoid creating a 0000-00-00 00:00:00 timestamp @@ -4534,6 +4518,18 @@ func (e *Executor) updateRowsCopied(ctx context.Context, uuid string, rowsCopied return err } +func (e *Executor) updateMigrationVreplicationLagSeconds(ctx context.Context, uuid string, vreplicationLagSeconds int64) error { + query, err := sqlparser.ParseAndBind(sqlUpdateMigrationVreplicationLagSeconds, + sqltypes.Int64BindVariable(vreplicationLagSeconds), + sqltypes.StringBindVariable(uuid), + ) + if err != nil { + return err + } + _, err = e.execQuery(ctx, query) + return err +} + func (e *Executor) updateVitessLivenessIndicator(ctx context.Context, uuid string, livenessIndicator int64) error { query, err := sqlparser.ParseAndBind(sqlUpdateMigrationVitessLivenessIndicator, sqltypes.Int64BindVariable(livenessIndicator), diff --git a/go/vt/vttablet/onlineddl/schema.go b/go/vt/vttablet/onlineddl/schema.go index bc3fa4f2bc9..943a3b1df07 100644 --- a/go/vt/vttablet/onlineddl/schema.go +++ b/go/vt/vttablet/onlineddl/schema.go @@ -87,6 +87,11 @@ const ( WHERE migration_uuid=%a ` + sqlUpdateMigrationVreplicationLagSeconds = `UPDATE _vt.schema_migrations + SET vreplication_lag_seconds=%a + WHERE + migration_uuid=%a + ` sqlUpdateMigrationIsView = `UPDATE _vt.schema_migrations SET is_view=%a WHERE diff --git a/go/vt/vttablet/onlineddl/vrepl.go b/go/vt/vttablet/onlineddl/vrepl.go index 26eb614e95a..2761c27c801 100644 --- a/go/vt/vttablet/onlineddl/vrepl.go +++ b/go/vt/vttablet/onlineddl/vrepl.go @@ -30,6 +30,7 @@ import ( "net/url" "strconv" "strings" + "time" "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/mysql/collations/charset" @@ -96,6 +97,19 @@ func (v *VReplStream) hasError() (isTerminal bool, vreplError error) { return false, nil } +// Lag returns the vreplication lag, as determined by the higher of the transaction timestamp and the time updated. +func (s *VReplStream) Lag() time.Duration { + durationDiff := func(t1, t2 time.Time) time.Duration { + return t1.Sub(t2).Abs() + } + timeNow := time.Now() + timeUpdated := time.Unix(s.timeUpdated, 0) + // Let's look at transaction timestamp. This gets written by any ongoing + // writes on the server (whether on this table or any other table) + transactionTimestamp := time.Unix(s.transactionTimestamp, 0) + return max(durationDiff(timeNow, timeUpdated), durationDiff(timeNow, transactionTimestamp)) +} + // VRepl is an online DDL helper for VReplication based migrations (ddl_strategy="online") type VRepl struct { workflow string diff --git a/go/vt/vttablet/tabletmanager/vreplication/replicator_plan.go b/go/vt/vttablet/tabletmanager/vreplication/replicator_plan.go index 6a416cb4414..62d6166b5ca 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/replicator_plan.go +++ b/go/vt/vttablet/tabletmanager/vreplication/replicator_plan.go @@ -618,49 +618,40 @@ func valsEqual(v1, v2 sqltypes.Value) bool { func (tp *TablePlan) appendFromRow(buf *bytes2.Buffer, row *querypb.Row) error { bindLocations := tp.BulkInsertValues.BindLocations() if len(tp.Fields) < len(bindLocations) { - return vterrors.Errorf(vtrpcpb.Code_INTERNAL, "wrong number of fields: got %d fields for %d bind locations ", + return vterrors.Errorf(vtrpcpb.Code_INTERNAL, "wrong number of fields: got %d fields for %d bind locations", len(tp.Fields), len(bindLocations)) } - type colInfo struct { - typ querypb.Type - length int64 - offset int64 - field *querypb.Field - } - rowInfo := make([]*colInfo, 0) - - offset := int64(0) - for i, field := range tp.Fields { // collect info required for fields to be bound - length := row.Lengths[i] - if !tp.FieldsToSkip[strings.ToLower(field.Name)] { - rowInfo = append(rowInfo, &colInfo{ - typ: field.Type, - length: length, - offset: offset, - field: field, - }) - } - if length > 0 { - offset += row.Lengths[i] + // Bind field values to locations. + var ( + offset int64 + offsetQuery int + fieldsIndex int + field *querypb.Field + ) + for i, loc := range bindLocations { + field = tp.Fields[fieldsIndex] + length := row.Lengths[fieldsIndex] + for tp.FieldsToSkip[strings.ToLower(field.Name)] { + if length > 0 { + offset += length + } + fieldsIndex++ + field = tp.Fields[fieldsIndex] + length = row.Lengths[fieldsIndex] } - } - // bind field values to locations - var offsetQuery int - for i, loc := range bindLocations { - col := rowInfo[i] buf.WriteString(tp.BulkInsertValues.Query[offsetQuery:loc.Offset]) - typ := col.typ + typ := field.Type switch typ { case querypb.Type_TUPLE: return vterrors.Errorf(vtrpcpb.Code_INTERNAL, "unexpected Type_TUPLE for value %d", i) case querypb.Type_JSON: - if col.length < 0 { // An SQL NULL and not an actual JSON value + if length < 0 { // An SQL NULL and not an actual JSON value buf.WriteString(sqltypes.NullStr) } else { // A JSON value (which may be a JSON null literal value) - buf2 := row.Values[col.offset : col.offset+col.length] + buf2 := row.Values[offset : offset+length] vv, err := vjson.MarshalSQLValue(buf2) if err != nil { return err @@ -668,16 +659,16 @@ func (tp *TablePlan) appendFromRow(buf *bytes2.Buffer, row *querypb.Row) error { buf.WriteString(vv.RawStr()) } default: - if col.length < 0 { + if length < 0 { // -1 means a null variable; serialize it directly buf.WriteString(sqltypes.NullStr) } else { - raw := row.Values[col.offset : col.offset+col.length] + raw := row.Values[offset : offset+length] var vv sqltypes.Value - if conversion, ok := tp.ConvertCharset[col.field.Name]; ok && col.length > 0 { + if conversion, ok := tp.ConvertCharset[field.Name]; ok && length > 0 { // Non-null string value, for which we have a charset conversion instruction - out, err := tp.convertStringCharset(raw, conversion, col.field.Name) + out, err := tp.convertStringCharset(raw, conversion, field.Name) if err != nil { return err } @@ -690,6 +681,10 @@ func (tp *TablePlan) appendFromRow(buf *bytes2.Buffer, row *querypb.Row) error { } } offsetQuery = loc.Offset + loc.Length + if length > 0 { + offset += length + } + fieldsIndex++ } buf.WriteString(tp.BulkInsertValues.Query[offsetQuery:]) return nil diff --git a/go/vt/vttablet/tabletmanager/vreplication/replicator_plan_test.go b/go/vt/vttablet/tabletmanager/vreplication/replicator_plan_test.go index 644b4585914..09ace916f11 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/replicator_plan_test.go +++ b/go/vt/vttablet/tabletmanager/vreplication/replicator_plan_test.go @@ -21,17 +21,18 @@ import ( "strings" "testing" - vttablet "vitess.io/vitess/go/vt/vttablet/common" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "vitess.io/vitess/go/bytes2" "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/binlog/binlogplayer" "vitess.io/vitess/go/vt/sqlparser" binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" + querypb "vitess.io/vitess/go/vt/proto/query" + vttablet "vitess.io/vitess/go/vt/vttablet/common" ) type TestReplicatorPlan struct { @@ -829,3 +830,137 @@ func TestBuildPlayerPlanExclude(t *testing.T) { wantPlan, _ := json.Marshal(want) assert.Equal(t, string(gotPlan), string(wantPlan)) } + +func TestAppendFromRow(t *testing.T) { + testCases := []struct { + name string + tp *TablePlan + row *querypb.Row + want string + wantErr string + }{ + { + name: "simple", + tp: &TablePlan{ + BulkInsertValues: sqlparser.BuildParsedQuery("values (%a, %a, %a)", + ":c1", ":c2", ":c3", + ), + Fields: []*querypb.Field{ + {Name: "c1", Type: querypb.Type_INT32}, + {Name: "c2", Type: querypb.Type_INT32}, + {Name: "c3", Type: querypb.Type_INT32}, + }, + }, + row: sqltypes.RowToProto3( + []sqltypes.Value{ + sqltypes.NewInt64(1), + sqltypes.NewInt64(2), + sqltypes.NewInt64(3), + }, + ), + want: "values (1, 2, 3)", + }, + { + name: "too few fields", + tp: &TablePlan{ + BulkInsertValues: sqlparser.BuildParsedQuery("values (%a, %a, %a)", + ":c1", ":c2", ":c3", + ), + Fields: []*querypb.Field{ + {Name: "c1", Type: querypb.Type_INT32}, + {Name: "c2", Type: querypb.Type_INT32}, + }, + }, + wantErr: "wrong number of fields: got 2 fields for 3 bind locations", + }, + { + name: "skip half", + tp: &TablePlan{ + BulkInsertValues: sqlparser.BuildParsedQuery("values (%a, %a, %a, %a)", + ":c1", ":c2", ":c4", ":c8", + ), + Fields: []*querypb.Field{ + {Name: "c1", Type: querypb.Type_INT32}, + {Name: "c2", Type: querypb.Type_INT32}, + {Name: "c3", Type: querypb.Type_INT32}, + {Name: "c4", Type: querypb.Type_INT32}, + {Name: "c5", Type: querypb.Type_INT32}, + {Name: "c6", Type: querypb.Type_INT32}, + {Name: "c7", Type: querypb.Type_INT32}, + {Name: "c8", Type: querypb.Type_INT32}, + }, + FieldsToSkip: map[string]bool{ + "c3": true, + "c5": true, + "c6": true, + "c7": true, + }, + }, + row: sqltypes.RowToProto3( + []sqltypes.Value{ + sqltypes.NewInt64(1), + sqltypes.NewInt64(2), + sqltypes.NewInt64(3), + sqltypes.NewInt64(4), + sqltypes.NewInt64(5), + sqltypes.NewInt64(6), + sqltypes.NewInt64(7), + sqltypes.NewInt64(8), + }, + ), + want: "values (1, 2, 4, 8)", + }, + { + name: "skip all but one", + tp: &TablePlan{ + BulkInsertValues: sqlparser.BuildParsedQuery("values (%a)", + ":c4", + ), + Fields: []*querypb.Field{ + {Name: "c1", Type: querypb.Type_INT32}, + {Name: "c2", Type: querypb.Type_INT32}, + {Name: "c3", Type: querypb.Type_INT32}, + {Name: "c4", Type: querypb.Type_INT32}, + {Name: "c5", Type: querypb.Type_INT32}, + {Name: "c6", Type: querypb.Type_INT32}, + {Name: "c7", Type: querypb.Type_INT32}, + {Name: "c8", Type: querypb.Type_INT32}, + }, + FieldsToSkip: map[string]bool{ + "c1": true, + "c2": true, + "c3": true, + "c5": true, + "c6": true, + "c7": true, + "c8": true, + }, + }, + row: sqltypes.RowToProto3( + []sqltypes.Value{ + sqltypes.NewInt64(1), + sqltypes.NewInt64(2), + sqltypes.NewInt64(3), + sqltypes.NewInt64(4), + sqltypes.NewInt64(5), + sqltypes.NewInt64(6), + sqltypes.NewInt64(7), + sqltypes.NewInt64(8), + }, + ), + want: "values (4)", + }, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + bb := &bytes2.Buffer{} + err := tc.tp.appendFromRow(bb, tc.row) + if tc.wantErr != "" { + require.EqualError(t, err, tc.wantErr) + } else { + require.NoError(t, err) + require.Equal(t, tc.want, bb.String()) + } + }) + } +} diff --git a/go/vt/vttablet/tabletmanager/vreplication/vdbclient.go b/go/vt/vttablet/tabletmanager/vreplication/vdbclient.go index b8339cdf874..8a4409db06c 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/vdbclient.go +++ b/go/vt/vttablet/tabletmanager/vreplication/vdbclient.go @@ -171,7 +171,7 @@ func (vc *vdbClient) Execute(query string) (*sqltypes.Result, error) { func (vc *vdbClient) ExecuteWithRetry(ctx context.Context, query string) (*sqltypes.Result, error) { qr, err := vc.Execute(query) for err != nil { - if sqlErr, ok := err.(*sqlerror.SQLError); ok && sqlErr.Number() == sqlerror.ERLockDeadlock || sqlErr.Number() == sqlerror.ERLockWaitTimeout { + if sqlErr, ok := err.(*sqlerror.SQLError); ok && (sqlErr.Number() == sqlerror.ERLockDeadlock || sqlErr.Number() == sqlerror.ERLockWaitTimeout) { log.Infof("retryable error: %v, waiting for %v and retrying", sqlErr, dbLockRetryDelay) if err := vc.Rollback(); err != nil { return nil, err diff --git a/go/vt/vttablet/tabletserver/debugenv.go b/go/vt/vttablet/tabletserver/debugenv.go index 54cf09db7d6..6f1ea854ea9 100644 --- a/go/vt/vttablet/tabletserver/debugenv.go +++ b/go/vt/vttablet/tabletserver/debugenv.go @@ -23,9 +23,10 @@ import ( "html" "net/http" "strconv" - "text/template" "time" + "github.com/google/safehtml/template" + "vitess.io/vitess/go/acl" "vitess.io/vitess/go/vt/log" ) @@ -70,90 +71,131 @@ func debugEnvHandler(tsv *TabletServer, w http.ResponseWriter, r *http.Request) return } + switch r.Method { + case http.MethodPost: + handlePost(tsv, w, r) + case http.MethodGet: + handleGet(tsv, w, r) + default: + http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) + } +} + +func handlePost(tsv *TabletServer, w http.ResponseWriter, r *http.Request) { + varname := r.FormValue("varname") + value := r.FormValue("value") + var msg string - if r.Method == "POST" { - varname := r.FormValue("varname") - value := r.FormValue("value") - setIntVal := func(f func(int)) { - ival, err := strconv.Atoi(value) - if err != nil { - msg = fmt.Sprintf("Failed setting value for %v: %v", varname, err) - return - } - f(ival) - msg = fmt.Sprintf("Setting %v to: %v", varname, value) + if varname == "" || value == "" { + http.Error(w, "Missing varname or value", http.StatusBadRequest) + return + } + + setIntVal := func(f func(int)) error { + ival, err := strconv.Atoi(value) + if err != nil { + return fmt.Errorf("invalid int value for %v: %v", varname, err) } - setIntValCtx := func(f func(context.Context, int) error) { - ival, err := strconv.Atoi(value) - if err == nil { - err = f(r.Context(), ival) - if err == nil { - msg = fmt.Sprintf("Setting %v to: %v", varname, value) - return - } - } - msg = fmt.Sprintf("Failed setting value for %v: %v", varname, err) + f(ival) + msg = fmt.Sprintf("Setting %v to: %v", varname, value) + return nil + } + + setIntValCtx := func(f func(context.Context, int) error) error { + ival, err := strconv.Atoi(value) + if err == nil { + err = f(r.Context(), ival) } - setInt64Val := func(f func(int64)) { - ival, err := strconv.ParseInt(value, 10, 64) - if err != nil { - msg = fmt.Sprintf("Failed setting value for %v: %v", varname, err) - return - } - f(ival) - msg = fmt.Sprintf("Setting %v to: %v", varname, value) + if err != nil { + return fmt.Errorf("failed setting value for %v: %v", varname, err) } - setDurationVal := func(f func(time.Duration)) { - durationVal, err := time.ParseDuration(value) - if err != nil { - msg = fmt.Sprintf("Failed setting value for %v: %v", varname, err) - return - } - f(durationVal) - msg = fmt.Sprintf("Setting %v to: %v", varname, value) + msg = fmt.Sprintf("Setting %v to: %v", varname, value) + return nil + } + + setInt64Val := func(f func(int64)) error { + ival, err := strconv.ParseInt(value, 10, 64) + if err != nil { + return fmt.Errorf("invalid int64 value for %v: %v", varname, err) } - setFloat64Val := func(f func(float64)) { - fval, err := strconv.ParseFloat(value, 64) - if err != nil { - msg = fmt.Sprintf("Failed setting value for %v: %v", varname, err) - return - } - f(fval) - msg = fmt.Sprintf("Setting %v to: %v", varname, value) + f(ival) + msg = fmt.Sprintf("Setting %v to: %v", varname, value) + return nil + } + + setDurationVal := func(f func(time.Duration)) error { + durationVal, err := time.ParseDuration(value) + if err != nil { + return fmt.Errorf("invalid duration value for %v: %v", varname, err) } - switch varname { - case "PoolSize": - setIntValCtx(tsv.SetPoolSize) - case "StreamPoolSize": - setIntValCtx(tsv.SetStreamPoolSize) - case "TxPoolSize": - setIntValCtx(tsv.SetTxPoolSize) - case "MaxResultSize": - setIntVal(tsv.SetMaxResultSize) - case "WarnResultSize": - setIntVal(tsv.SetWarnResultSize) - case "RowStreamerMaxInnoDBTrxHistLen": - setInt64Val(func(val int64) { tsv.Config().RowStreamer.MaxInnoDBTrxHistLen = val }) - case "RowStreamerMaxMySQLReplLagSecs": - setInt64Val(func(val int64) { tsv.Config().RowStreamer.MaxMySQLReplLagSecs = val }) - case "UnhealthyThreshold": - setDurationVal(func(d time.Duration) { tsv.Config().Healthcheck.UnhealthyThreshold = d }) - setDurationVal(tsv.hs.SetUnhealthyThreshold) - setDurationVal(tsv.sm.SetUnhealthyThreshold) - case "ThrottleMetricThreshold": - setFloat64Val(tsv.SetThrottleMetricThreshold) - case "Consolidator": - tsv.SetConsolidatorMode(value) - msg = fmt.Sprintf("Setting %v to: %v", varname, value) + f(durationVal) + msg = fmt.Sprintf("Setting %v to: %v", varname, value) + return nil + } + + setFloat64Val := func(f func(float64)) error { + fval, err := strconv.ParseFloat(value, 64) + if err != nil { + return fmt.Errorf("invalid float64 value for %v: %v", varname, err) } + f(fval) + msg = fmt.Sprintf("Setting %v to: %v", varname, value) + return nil + } + + var err error + switch varname { + case "ReadPoolSize": + err = setIntValCtx(tsv.SetPoolSize) + case "StreamPoolSize": + err = setIntValCtx(tsv.SetStreamPoolSize) + case "TransactionPoolSize": + err = setIntValCtx(tsv.SetTxPoolSize) + case "MaxResultSize": + err = setIntVal(tsv.SetMaxResultSize) + case "WarnResultSize": + err = setIntVal(tsv.SetWarnResultSize) + case "RowStreamerMaxInnoDBTrxHistLen": + err = setInt64Val(func(val int64) { tsv.Config().RowStreamer.MaxInnoDBTrxHistLen = val }) + case "RowStreamerMaxMySQLReplLagSecs": + err = setInt64Val(func(val int64) { tsv.Config().RowStreamer.MaxMySQLReplLagSecs = val }) + case "UnhealthyThreshold": + err = setDurationVal(func(d time.Duration) { tsv.Config().Healthcheck.UnhealthyThreshold = d }) + case "ThrottleMetricThreshold": + err = setFloat64Val(tsv.SetThrottleMetricThreshold) + case "Consolidator": + tsv.SetConsolidatorMode(value) + msg = fmt.Sprintf("Setting %v to: %v", varname, value) + } + + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return } + vars := getVars(tsv) + sendResponse(r, w, vars, msg) +} + +func handleGet(tsv *TabletServer, w http.ResponseWriter, r *http.Request) { + vars := getVars(tsv) + sendResponse(r, w, vars, "") +} + +func sendResponse(r *http.Request, w http.ResponseWriter, vars []envValue, msg string) { + format := r.FormValue("format") + if format == "json" { + respondWithJSON(w, vars, msg) + return + } + respondWithHTML(w, vars, msg) +} + +func getVars(tsv *TabletServer) []envValue { var vars []envValue - vars = addVar(vars, "PoolSize", tsv.PoolSize) + vars = addVar(vars, "ReadPoolSize", tsv.PoolSize) vars = addVar(vars, "StreamPoolSize", tsv.StreamPoolSize) - vars = addVar(vars, "TxPoolSize", tsv.TxPoolSize) - vars = addVar(vars, "QueryCacheCapacity", tsv.QueryPlanCacheCap) // QueryCacheCapacity is deprecated in v21, it is replaced by QueryEnginePlanCacheCapacity - vars = addVar(vars, "QueryEnginePlanCacheCapacity", tsv.QueryPlanCacheCap) + vars = addVar(vars, "TransactionPoolSize", tsv.TxPoolSize) vars = addVar(vars, "MaxResultSize", tsv.MaxResultSize) vars = addVar(vars, "WarnResultSize", tsv.WarnResultSize) vars = addVar(vars, "RowStreamerMaxInnoDBTrxHistLen", func() int64 { return tsv.Config().RowStreamer.MaxInnoDBTrxHistLen }) @@ -165,18 +207,22 @@ func debugEnvHandler(tsv *TabletServer, w http.ResponseWriter, r *http.Request) Value: tsv.ConsolidatorMode(), }) - format := r.FormValue("format") - if format == "json" { - mvars := make(map[string]string) - for _, v := range vars { - mvars[v.Name] = v.Value - } - w.Header().Set("Content-Type", "application/json") - _ = json.NewEncoder(w).Encode(mvars) - return + return vars +} + +func respondWithJSON(w http.ResponseWriter, vars []envValue, msg string) { + mvars := make(map[string]string) + for _, v := range vars { + mvars[v.Name] = v.Value } + if msg != "" { + mvars["ResponseMessage"] = msg + } + w.Header().Set("Content-Type", "application/json") + _ = json.NewEncoder(w).Encode(mvars) +} - // gridTable is reused from twopcz.go. +func respondWithHTML(w http.ResponseWriter, vars []envValue, msg string) { w.Write(gridTable) w.Write([]byte("

Internal Variables

\n")) if msg != "" { diff --git a/go/vt/vttablet/tabletserver/querylogz.go b/go/vt/vttablet/tabletserver/querylogz.go index 33341d1641b..09f375aa329 100644 --- a/go/vt/vttablet/tabletserver/querylogz.go +++ b/go/vt/vttablet/tabletserver/querylogz.go @@ -20,9 +20,10 @@ import ( "net/http" "strconv" "strings" - "text/template" "time" + "github.com/google/safehtml/template" + "vitess.io/vitess/go/acl" "vitess.io/vitess/go/vt/log" "vitess.io/vitess/go/vt/logz" diff --git a/go/vt/vttablet/tabletserver/querylogz_test.go b/go/vt/vttablet/tabletserver/querylogz_test.go index 25f03c762c7..ee26437f330 100644 --- a/go/vt/vttablet/tabletserver/querylogz_test.go +++ b/go/vt/vttablet/tabletserver/querylogz_test.go @@ -37,7 +37,7 @@ func TestQuerylogzHandler(t *testing.T) { req, _ := http.NewRequest("GET", "/querylogz?timeout=10&limit=1", nil) logStats := tabletenv.NewLogStats(context.Background(), "Execute") logStats.PlanType = planbuilder.PlanSelect.String() - logStats.OriginalSQL = "select name from test_table limit 1000" + logStats.OriginalSQL = "select name, 'inject ' from test_table limit 1000" logStats.RowsAffected = 1000 logStats.NumberOfQueries = 1 logStats.StartTime, _ = time.Parse("Jan 2 15:04:05", "Nov 29 13:33:09") @@ -64,7 +64,7 @@ func TestQuerylogzHandler(t *testing.T) { `0.001`, `1e-08`, `Select`, - `select name from test_table limit 1000`, + regexp.QuoteMeta(`select name,​ 'inject <script>alert()​;</script>' from test_table limit 1000`), `1`, `none`, `1000`, @@ -95,7 +95,7 @@ func TestQuerylogzHandler(t *testing.T) { `0.001`, `1e-08`, `Select`, - `select name from test_table limit 1000`, + regexp.QuoteMeta(`select name,​ 'inject <script>alert()​;</script>' from test_table limit 1000`), `1`, `none`, `1000`, @@ -126,7 +126,7 @@ func TestQuerylogzHandler(t *testing.T) { `0.001`, `1e-08`, `Select`, - `select name from test_table limit 1000`, + regexp.QuoteMeta(`select name,​ 'inject <script>alert()​;</script>' from test_table limit 1000`), `1`, `none`, `1000`, diff --git a/go/vt/vttablet/tabletserver/throttle/base/metric_name.go b/go/vt/vttablet/tabletserver/throttle/base/metric_name.go index 607192b9c0c..43bd2d17a8c 100644 --- a/go/vt/vttablet/tabletserver/throttle/base/metric_name.go +++ b/go/vt/vttablet/tabletserver/throttle/base/metric_name.go @@ -65,6 +65,7 @@ const ( ThreadsRunningMetricName MetricName = "threads_running" CustomMetricName MetricName = "custom" LoadAvgMetricName MetricName = "loadavg" + HistoryListLengthMetricName MetricName = "history_list_length" MysqldLoadAvgMetricName MetricName = "mysqld-loadavg" MysqldDatadirUsedRatioMetricName MetricName = "mysqld-datadir-used-ratio" ) diff --git a/go/vt/vttablet/tabletserver/throttle/base/metric_name_test.go b/go/vt/vttablet/tabletserver/throttle/base/metric_name_test.go index ffd7f674cc2..c2e2b44b36f 100644 --- a/go/vt/vttablet/tabletserver/throttle/base/metric_name_test.go +++ b/go/vt/vttablet/tabletserver/throttle/base/metric_name_test.go @@ -241,15 +241,17 @@ func TestKnownMetricNames(t *testing.T) { assert.Contains(t, KnownMetricNames, LoadAvgMetricName) assert.Contains(t, KnownMetricNames, CustomMetricName) assert.Contains(t, KnownMetricNames, DefaultMetricName) + assert.Contains(t, KnownMetricNames, HistoryListLengthMetricName) assert.Contains(t, KnownMetricNames, MysqldLoadAvgMetricName) assert.Contains(t, KnownMetricNames, MysqldDatadirUsedRatioMetricName) } -func TestSingleWordCamelKnownMetricNames(t *testing.T) { +func TestKnownMetricNamesPascalCase(t *testing.T) { expectCases := map[MetricName]string{ LagMetricName: "Lag", ThreadsRunningMetricName: "ThreadsRunning", LoadAvgMetricName: "Loadavg", + HistoryListLengthMetricName: "HistoryListLength", CustomMetricName: "Custom", DefaultMetricName: "Default", MysqldLoadAvgMetricName: "MysqldLoadavg", diff --git a/go/vt/vttablet/tabletserver/throttle/base/self_metric_innodb_history_list_length.go b/go/vt/vttablet/tabletserver/throttle/base/self_metric_innodb_history_list_length.go new file mode 100644 index 00000000000..2696b1750ea --- /dev/null +++ b/go/vt/vttablet/tabletserver/throttle/base/self_metric_innodb_history_list_length.go @@ -0,0 +1,68 @@ +/* +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 base + +import ( + "context" + "math" + "sync/atomic" + "time" +) + +var ( + historyListLengthQuery = "select count as history_len from information_schema.INNODB_METRICS where name = 'trx_rseg_history_len'" + + cachedHistoryListLengthMetric atomic.Pointer[ThrottleMetric] + historyListLengthCacheDuration = 5 * time.Second + historyListLengthDefaultThreshold = math.Pow10(9) +) + +var _ SelfMetric = registerSelfMetric(&HistoryListLengthSelfMetric{}) + +type HistoryListLengthSelfMetric struct { +} + +func (m *HistoryListLengthSelfMetric) Name() MetricName { + return HistoryListLengthMetricName +} + +func (m *HistoryListLengthSelfMetric) DefaultScope() Scope { + return SelfScope +} + +func (m *HistoryListLengthSelfMetric) DefaultThreshold() float64 { + return historyListLengthDefaultThreshold +} + +func (m *HistoryListLengthSelfMetric) RequiresConn() bool { + return true +} + +func (m *HistoryListLengthSelfMetric) Read(ctx context.Context, params *SelfMetricReadParams) *ThrottleMetric { + // This function will be called sequentially, and therefore does not need strong mutex protection. Still, we use atomics + // to ensure correctness in case an external goroutine tries to read the metric concurrently. + metric := cachedHistoryListLengthMetric.Load() + if metric != nil { + return metric + } + metric = ReadSelfMySQLThrottleMetric(ctx, params.Conn, historyListLengthQuery) + cachedHistoryListLengthMetric.Store(metric) + time.AfterFunc(historyListLengthCacheDuration, func() { + cachedHistoryListLengthMetric.Store(nil) + }) + return metric +} diff --git a/go/vt/vttablet/tabletserver/throttle/throttler_test.go b/go/vt/vttablet/tabletserver/throttle/throttler_test.go index 0a1162b02d3..352e641fa35 100644 --- a/go/vt/vttablet/tabletserver/throttle/throttler_test.go +++ b/go/vt/vttablet/tabletserver/throttle/throttler_test.go @@ -71,6 +71,12 @@ var ( Value: 2.718, Err: nil, }, + base.HistoryListLengthMetricName: &base.ThrottleMetric{ + Scope: base.SelfScope, + Alias: "", + Value: 5, + Err: nil, + }, base.MysqldLoadAvgMetricName: &base.ThrottleMetric{ Scope: base.SelfScope, Alias: "", @@ -105,6 +111,11 @@ var ( ResponseCode: tabletmanagerdatapb.CheckThrottlerResponseCode_OK, Value: 5.1, }, + base.HistoryListLengthMetricName.String(): { + StatusCode: http.StatusOK, + ResponseCode: tabletmanagerdatapb.CheckThrottlerResponseCode_OK, + Value: 6, + }, base.MysqldLoadAvgMetricName.String(): { StatusCode: http.StatusOK, ResponseCode: tabletmanagerdatapb.CheckThrottlerResponseCode_OK, @@ -1853,6 +1864,7 @@ func TestChecks(t *testing.T) { assert.EqualValues(t, 26, checkResult.Metrics[base.ThreadsRunningMetricName.String()].Value) // self value, because flags.Scope is set assert.EqualValues(t, 17, checkResult.Metrics[base.CustomMetricName.String()].Value) // self value, because flags.Scope is set assert.EqualValues(t, 2.718, checkResult.Metrics[base.LoadAvgMetricName.String()].Value) // self value, because flags.Scope is set + assert.EqualValues(t, 5, checkResult.Metrics[base.HistoryListLengthMetricName.String()].Value) // self value, because flags.Scope is set assert.EqualValues(t, 0.3311, checkResult.Metrics[base.MysqldLoadAvgMetricName.String()].Value) // self value, because flags.Scope is set assert.EqualValues(t, 0.85, checkResult.Metrics[base.MysqldDatadirUsedRatioMetricName.String()].Value) // self value, because flags.Scope is set for _, metric := range checkResult.Metrics { @@ -1914,6 +1926,7 @@ func TestChecks(t *testing.T) { assert.EqualValues(t, 26, checkResult.Metrics[base.ThreadsRunningMetricName.String()].Value) // shard value, because flags.Scope is set assert.EqualValues(t, 17, checkResult.Metrics[base.CustomMetricName.String()].Value) // shard value, because flags.Scope is set assert.EqualValues(t, 5.1, checkResult.Metrics[base.LoadAvgMetricName.String()].Value) // shard value, because flags.Scope is set + assert.EqualValues(t, 6, checkResult.Metrics[base.HistoryListLengthMetricName.String()].Value) // shard value, because flags.Scope is set assert.EqualValues(t, 0.3311, checkResult.Metrics[base.MysqldLoadAvgMetricName.String()].Value) // shard value, because flags.Scope is set assert.EqualValues(t, 0.87, checkResult.Metrics[base.MysqldDatadirUsedRatioMetricName.String()].Value) // shard value, because flags.Scope is set for _, metric := range checkResult.Metrics { @@ -1948,6 +1961,7 @@ func TestChecks(t *testing.T) { assert.EqualValues(t, 26, checkResult.Metrics[base.ThreadsRunningMetricName.String()].Value) // self value, because "self" is the default scope for threads_running assert.EqualValues(t, 17, checkResult.Metrics[base.CustomMetricName.String()].Value) // self value, because "self" is the default scope for custom assert.EqualValues(t, 2.718, checkResult.Metrics[base.LoadAvgMetricName.String()].Value) // self value, because "self" is the default scope for loadavg + assert.EqualValues(t, 5, checkResult.Metrics[base.HistoryListLengthMetricName.String()].Value) // self value, because "self" is the default scope for loadavg assert.EqualValues(t, 0.3311, checkResult.Metrics[base.MysqldLoadAvgMetricName.String()].Value) // self value, because "self" is the default scope for loadavg assert.EqualValues(t, 0.85, checkResult.Metrics[base.MysqldDatadirUsedRatioMetricName.String()].Value) // self value, because "self" is the default scope for loadavg assert.EqualValues(t, base.ShardScope.String(), checkResult.Metrics[base.LagMetricName.String()].Scope) @@ -1970,6 +1984,7 @@ func TestChecks(t *testing.T) { base.MetricName("custom"), base.MetricName("shard/loadavg"), base.MetricName("shard/mysqld-loadavg"), + base.MetricName("self/history_list_length"), base.MetricName("self/mysqld-datadir-used-ratio"), base.MetricName("default"), } @@ -1986,6 +2001,7 @@ func TestChecks(t *testing.T) { assert.EqualValues(t, 26, checkResult.Metrics[base.ThreadsRunningMetricName.String()].Value) // shard value, even though scope name is in metric name assert.EqualValues(t, 17, checkResult.Metrics[base.CustomMetricName.String()].Value) // shard value because flags.Scope is set assert.EqualValues(t, 5.1, checkResult.Metrics[base.LoadAvgMetricName.String()].Value) // shard value, not because scope name is in metric name but because flags.Scope is set + assert.EqualValues(t, 6, checkResult.Metrics[base.HistoryListLengthMetricName.String()].Value) // shard value, even though scope name is in metric name assert.EqualValues(t, 0.3311, checkResult.Metrics[base.MysqldLoadAvgMetricName.String()].Value) // shard value, not because scope name is in metric name but because flags.Scope is set assert.EqualValues(t, 0.87, checkResult.Metrics[base.MysqldDatadirUsedRatioMetricName.String()].Value) // shard value, even though scope name is in metric name for _, metric := range checkResult.Metrics { @@ -2257,6 +2273,7 @@ func TestReplica(t *testing.T) { assert.Error(t, metricResult.Error, "metricName=%v, value=%v, threshold=%v", metricName, metricResult.Value, metricResult.Threshold) assert.ErrorIs(t, metricResult.Error, base.ErrThresholdExceeded) case base.ThreadsRunningMetricName, + base.HistoryListLengthMetricName, base.MysqldLoadAvgMetricName, base.MysqldDatadirUsedRatioMetricName: assert.NoError(t, metricResult.Error, "metricName=%v, value=%v, threshold=%v", metricName, metricResult.Value, metricResult.Threshold) diff --git a/go/vt/vttablet/tabletserver/vstreamer/planbuilder.go b/go/vt/vttablet/tabletserver/vstreamer/planbuilder.go index 9bbc98ca2bd..e5115afe6d3 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/planbuilder.go +++ b/go/vt/vttablet/tabletserver/vstreamer/planbuilder.go @@ -89,6 +89,8 @@ const ( NotEqual // IsNotNull is used to filter a column if it is NULL IsNotNull + // In is used to filter a comparable column if equals any of the values from a specific tuple + In ) // Filter contains opcodes for filtering. @@ -97,6 +99,9 @@ type Filter struct { ColNum int Value sqltypes.Value + // Values will be used to store tuple/list values. + Values []sqltypes.Value + // Parameters for VindexMatch. // Vindex, VindexColumns and KeyRange, if set, will be used // to filter the row. @@ -166,6 +171,8 @@ func getOpcode(comparison *sqlparser.ComparisonExpr) (Opcode, error) { opcode = GreaterThanEqual case sqlparser.NotEqualOp: opcode = NotEqual + case sqlparser.InOp: + opcode = In default: return -1, fmt.Errorf("comparison operator %s not supported", comparison.Operator.ToString()) } @@ -238,6 +245,24 @@ func (plan *Plan) filter(values, result []sqltypes.Value, charsets []collations. if values[filter.ColNum].IsNull() { return false, nil } + case In: + if filter.Values == nil { + return false, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "unexpected empty filter values when performing IN operator") + } + found := false + for _, filterValue := range filter.Values { + match, err := compare(Equal, values[filter.ColNum], filterValue, plan.env.CollationEnv(), charsets[filter.ColNum]) + if err != nil { + return false, err + } + if match { + found = true + break + } + } + if !found { + return false, nil + } default: match, err := compare(filter.Opcode, values[filter.ColNum], filter.Value, plan.env.CollationEnv(), charsets[filter.ColNum]) if err != nil { @@ -514,6 +539,27 @@ func (plan *Plan) getColumnFuncExpr(columnName string) *sqlparser.FuncExpr { return nil } +func (plan *Plan) appendTupleFilter(values sqlparser.ValTuple, opcode Opcode, colnum int) error { + pv, err := evalengine.Translate(values, &evalengine.Config{ + Collation: plan.env.CollationEnv().DefaultConnectionCharset(), + Environment: plan.env, + }) + if err != nil { + return err + } + env := evalengine.EmptyExpressionEnv(plan.env) + resolved, err := env.Evaluate(pv) + if err != nil { + return err + } + plan.Filters = append(plan.Filters, Filter{ + Opcode: opcode, + ColNum: colnum, + Values: resolved.TupleValues(), + }) + return nil +} + func (plan *Plan) analyzeWhere(vschema *localVSchema, where *sqlparser.Where) error { if where == nil { return nil @@ -537,6 +583,20 @@ func (plan *Plan) analyzeWhere(vschema *localVSchema, where *sqlparser.Where) er if err != nil { return err } + // The Right Expr is typically expected to be a Literal value, + // except for the IN operator, where a Tuple value is expected. + // Handle the IN operator case first. + if opcode == In { + values, ok := expr.Right.(sqlparser.ValTuple) + if !ok { + return fmt.Errorf("unexpected: %v", sqlparser.String(expr)) + } + err := plan.appendTupleFilter(values, opcode, colnum) + if err != nil { + return err + } + continue + } val, ok := expr.Right.(*sqlparser.Literal) if !ok { return fmt.Errorf("unexpected: %v", sqlparser.String(expr)) diff --git a/go/vt/vttablet/tabletserver/vstreamer/planbuilder_test.go b/go/vt/vttablet/tabletserver/vstreamer/planbuilder_test.go index ba345b2a00b..aba74368802 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/planbuilder_test.go +++ b/go/vt/vttablet/tabletserver/vstreamer/planbuilder_test.go @@ -710,9 +710,15 @@ func TestPlanBuilderFilterComparison(t *testing.T) { outFilters: []Filter{{Opcode: LessThan, ColNum: 0, Value: sqltypes.NewInt64(2)}, {Opcode: LessThanEqual, ColNum: 1, Value: sqltypes.NewVarChar("xyz")}, }, + }, { + name: "in-operator", + inFilter: "select * from t1 where id in (1, 2)", + outFilters: []Filter{ + {Opcode: In, ColNum: 0, Values: []sqltypes.Value{sqltypes.NewInt64(1), sqltypes.NewInt64(2)}}, + }, }, { name: "vindex-and-operators", - inFilter: "select * from t1 where in_keyrange(id, 'hash', '-80') and id = 2 and val <> 'xyz'", + inFilter: "select * from t1 where in_keyrange(id, 'hash', '-80') and id = 2 and val <> 'xyz' and id in (100, 30)", outFilters: []Filter{ { Opcode: VindexMatch, @@ -727,6 +733,7 @@ func TestPlanBuilderFilterComparison(t *testing.T) { }, {Opcode: Equal, ColNum: 0, Value: sqltypes.NewInt64(2)}, {Opcode: NotEqual, ColNum: 1, Value: sqltypes.NewVarChar("xyz")}, + {Opcode: In, ColNum: 0, Values: []sqltypes.Value{sqltypes.NewInt64(100), sqltypes.NewInt64(30)}}, }, }} diff --git a/go/vt/vttablet/tabletserver/vstreamer/vstreamer_test.go b/go/vt/vttablet/tabletserver/vstreamer/vstreamer_test.go index 846d62202e7..5282b5f372d 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/vstreamer_test.go +++ b/go/vt/vttablet/tabletserver/vstreamer/vstreamer_test.go @@ -1966,7 +1966,7 @@ func TestFilteredMultipleWhere(t *testing.T) { filter: &binlogdatapb.Filter{ Rules: []*binlogdatapb.Rule{{ Match: "t1", - Filter: "select id1, val from t1 where in_keyrange('-80') and id2 = 200 and id3 = 1000 and val = 'newton'", + Filter: "select id1, val from t1 where in_keyrange('-80') and id2 = 200 and id3 = 1000 and val = 'newton' and id1 in (1, 2, 129)", }}, }, customFieldEvents: true, @@ -1988,9 +1988,7 @@ func TestFilteredMultipleWhere(t *testing.T) { {spec: &TestRowEventSpec{table: "t1", changes: []TestRowChange{{after: []string{"2", "newton"}}}}}, }}, {"insert into t1 values (3, 100, 2000, 'kepler')", noEvents}, - {"insert into t1 values (128, 200, 1000, 'newton')", []TestRowEvent{ - {spec: &TestRowEventSpec{table: "t1", changes: []TestRowChange{{after: []string{"128", "newton"}}}}}, - }}, + {"insert into t1 values (128, 200, 1000, 'newton')", noEvents}, {"insert into t1 values (5, 200, 2000, 'kepler')", noEvents}, {"insert into t1 values (129, 200, 1000, 'kepler')", noEvents}, {"commit", nil}, @@ -2080,3 +2078,33 @@ func TestGeneratedInvisiblePrimaryKey(t *testing.T) { }} ts.Run() } + +func TestFilteredInOperator(t *testing.T) { + ts := &TestSpec{ + t: t, + ddls: []string{ + "create table t1(id1 int, id2 int, val varbinary(128), primary key(id1))", + }, + options: &TestSpecOptions{ + filter: &binlogdatapb.Filter{ + Rules: []*binlogdatapb.Rule{{ + Match: "t1", + Filter: "select id1, val from t1 where val in ('eee', 'bbb', 'ddd') and id1 in (4, 5)", + }}, + }, + }, + } + defer ts.Close() + ts.Init() + ts.fieldEvents["t1"].cols[1].skip = true + ts.tests = [][]*TestQuery{{ + {"begin", nil}, + {"insert into t1 values (1, 100, 'aaa')", noEvents}, + {"insert into t1 values (2, 200, 'bbb')", noEvents}, + {"insert into t1 values (3, 100, 'ccc')", noEvents}, + {"insert into t1 values (4, 200, 'ddd')", nil}, + {"insert into t1 values (5, 200, 'eee')", nil}, + {"commit", nil}, + }} + ts.Run() +} diff --git a/proto/binlogdata.proto b/proto/binlogdata.proto index 595760dcd52..e1df792776b 100644 --- a/proto/binlogdata.proto +++ b/proto/binlogdata.proto @@ -353,6 +353,10 @@ message FieldEvent { repeated query.Field fields = 2; string keyspace = 3; string shard = 4; + + // Field numbers in the gap between shard (4) and enum_set_string_values + // (25) are NOT reserved and can be used. + // Are ENUM and SET field values already mapped to strings in the ROW // events? This allows us to transition VTGate VStream consumers from // the pre v20 behavior of having to do this mapping themselves to the @@ -362,6 +366,9 @@ message FieldEvent { // vstreams managed by the vstreamManager. bool enum_set_string_values = 25; bool is_internal_table = 26; // set for sidecardb tables + + // Add new members in the field number gap between shard (4) and + // enum_set_string_values (25). } // ShardGtid contains the GTID position for one shard. diff --git a/proto/vtadmin.proto b/proto/vtadmin.proto index 78f086ec345..963d1fa5779 100644 --- a/proto/vtadmin.proto +++ b/proto/vtadmin.proto @@ -388,7 +388,11 @@ message WorkflowSwitchTrafficRequest { message ApplySchemaRequest { string cluster_id = 1; - vtctldata.ApplySchemaRequest request = 2; + // Request.Sql will be overriden by this Sql field. + string sql = 2; + // Request.CallerId will be overriden by this CallerId field. + string caller_id = 3; + vtctldata.ApplySchemaRequest request = 4; } message CancelSchemaMigrationRequest { diff --git a/proto/vtgate.proto b/proto/vtgate.proto index 5b080178218..aadf211f0a2 100644 --- a/proto/vtgate.proto +++ b/proto/vtgate.proto @@ -76,6 +76,8 @@ message Session { // reserved connection if a dedicated connection is needed int64 reserved_id = 4; bool vindex_only = 5; + // rows_affected tracks if any query has modified the rows. + bool rows_affected = 6; } // shard_sessions keep track of per-shard transaction info. repeated ShardSession shard_sessions = 2; diff --git a/test/config.json b/test/config.json index c911232ce74..1e278546c7a 100644 --- a/test/config.json +++ b/test/config.json @@ -1238,6 +1238,17 @@ "RetryMax": 1, "Tags": [] }, + "vtop_example": { + "File": "", + "Args": [], + "Command": [ + "test/vtop_example.sh" + ], + "Manual": false, + "Shard": "", + "RetryMax": 1, + "Tags": [] + }, "vtorc_primary_failure": { "File": "unused.go", "Args": ["vitess.io/vitess/go/test/endtoend/vtorc/primaryfailure"], diff --git a/test/templates/cluster_endtoend_test.tpl b/test/templates/cluster_endtoend_test.tpl index 01f4555e303..6fe58fae361 100644 --- a/test/templates/cluster_endtoend_test.tpl +++ b/test/templates/cluster_endtoend_test.tpl @@ -14,7 +14,7 @@ env: jobs: build: name: Run endtoend tests on {{.Name}} - runs-on: {{if .Cores16}}gh-hosted-runners-16cores-1{{else}}ubuntu-latest{{end}} + runs-on: {{if .Cores16}}gh-hosted-runners-16cores-1-24.04{{else}}ubuntu-24.04{{end}} steps: - name: Skip CI @@ -124,10 +124,19 @@ jobs: # 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 + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/test/templates/cluster_endtoend_test_docker.tpl b/test/templates/cluster_endtoend_test_docker.tpl index f68e4223c75..f7e8aa2c1d8 100644 --- a/test/templates/cluster_endtoend_test_docker.tpl +++ b/test/templates/cluster_endtoend_test_docker.tpl @@ -6,7 +6,7 @@ permissions: read-all jobs: build: name: Run endtoend tests on {{.Name}} - runs-on: {{if .Cores16}}gh-hosted-runners-16cores-1{{else}}ubuntu-latest{{end}} + runs-on: {{if .Cores16}}gh-hosted-runners-16cores-1-24.04{{else}}ubuntu-24.04{{end}} steps: - name: Skip CI diff --git a/test/templates/cluster_endtoend_test_mysql57.tpl b/test/templates/cluster_endtoend_test_mysql57.tpl index b51ffc9110e..f4152c939b0 100644 --- a/test/templates/cluster_endtoend_test_mysql57.tpl +++ b/test/templates/cluster_endtoend_test_mysql57.tpl @@ -19,7 +19,7 @@ env: jobs: build: name: Run endtoend tests on {{.Name}} - runs-on: {{if .Cores16}}gh-hosted-runners-16cores-1{{else}}ubuntu-latest{{end}} + runs-on: {{if .Cores16}}gh-hosted-runners-16cores-1-24.04{{else}}ubuntu-24.04{{end}} steps: - name: Skip CI @@ -126,13 +126,17 @@ jobs: # Get key to latest MySQL repo sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-1_all.deb # Bionic packages are still compatible for Jammy since there's no MySQL 5.7 # packages for Jammy. echo mysql-apt-config mysql-apt-config/repo-codename select bionic | sudo debconf-set-selections echo mysql-apt-config mysql-apt-config/select-server select mysql-5.7 | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update + # We have to install this old version of libaio1. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb sudo DEBIAN_FRONTEND="noninteractive" apt-get install -y mysql-client=5.7* mysql-community-server=5.7* mysql-server=5.7* libncurses6 sudo apt-get install -y make unzip g++ etcd-client etcd-server curl git wget eatmydata diff --git a/test/templates/cluster_vitess_tester.tpl b/test/templates/cluster_vitess_tester.tpl index f0b5838d8e8..b8d77754ba6 100644 --- a/test/templates/cluster_vitess_tester.tpl +++ b/test/templates/cluster_vitess_tester.tpl @@ -14,7 +14,7 @@ env: jobs: build: name: Run endtoend tests on {{.Name}} - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -93,7 +93,7 @@ jobs: # 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 + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 diff --git a/test/templates/unit_test.tpl b/test/templates/unit_test.tpl index c47b7a1d796..3704aebac4e 100644 --- a/test/templates/unit_test.tpl +++ b/test/templates/unit_test.tpl @@ -14,7 +14,7 @@ env: jobs: test: name: {{.Name}} - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -87,20 +87,20 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.unit_tests == 'true' run: | export DEBIAN_FRONTEND="noninteractive" - sudo apt-get -qq update + sudo apt-get update # Uninstall any previously installed MySQL first sudo systemctl stop apparmor - sudo DEBIAN_FRONTEND="noninteractive" apt-get -qq remove -y --purge mysql-server mysql-client mysql-common - sudo apt-get -qq -y autoremove - sudo apt-get -qq -y autoclean + sudo DEBIAN_FRONTEND="noninteractive" apt-get remove -y --purge mysql-server mysql-client mysql-common + sudo apt-get -y autoremove + sudo apt-get -y autoclean sudo deluser mysql sudo rm -rf /var/lib/mysql sudo rm -rf /etc/mysql # Get key to latest MySQL repo sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-1_all.deb {{if (eq .Platform "mysql57")}} # Bionic packages are still compatible for Jammy since there's no MySQL 5.7 @@ -108,25 +108,32 @@ jobs: echo mysql-apt-config mysql-apt-config/repo-codename select bionic | sudo debconf-set-selections echo mysql-apt-config mysql-apt-config/select-server select mysql-5.7 | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* - sudo apt-get -qq update - sudo DEBIAN_FRONTEND="noninteractive" apt-get -qq install -y mysql-client=5.7* mysql-community-server=5.7* mysql-server=5.7* libncurses6 + sudo apt-get update + # We have to install this old version of libaio1. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo DEBIAN_FRONTEND="noninteractive" apt-get install -y mysql-client=5.7* mysql-community-server=5.7* mysql-server=5.7* libncurses6 {{end}} {{if (eq .Platform "mysql80")}} 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 - sudo DEBIAN_FRONTEND="noninteractive" apt-get -qq install -y mysql-server mysql-client + sudo apt-get update + sudo DEBIAN_FRONTEND="noninteractive" apt-get install -y mysql-server mysql-client {{end}} {{if (eq .Platform "mysql84")}} echo mysql-apt-config mysql-apt-config/select-server select mysql-8.4-lts | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* - sudo apt-get -qq update - sudo DEBIAN_FRONTEND="noninteractive" apt-get -qq install -y mysql-server mysql-client + sudo apt-get update + sudo DEBIAN_FRONTEND="noninteractive" apt-get install -y mysql-server mysql-client {{end}} - sudo apt-get -qq install -y make unzip g++ curl git wget ant openjdk-11-jdk eatmydata + sudo apt-get install -y make unzip g++ curl git wget ant openjdk-11-jdk eatmydata sudo service mysql stop sudo bash -c "echo '/usr/sbin/mysqld { }' > /etc/apparmor.d/usr.sbin.mysqld" # https://bugs.launchpad.net/ubuntu/+source/mariadb-10.1/+bug/1806263 sudo ln -s /etc/apparmor.d/usr.sbin.mysqld /etc/apparmor.d/disable/ diff --git a/test/vtop_example.sh b/test/vtop_example.sh index 5ff90a2be7e..c537c0f844c 100755 --- a/test/vtop_example.sh +++ b/test/vtop_example.sh @@ -482,11 +482,12 @@ EOF waitForKeyspaceToBeServing customer 80- 1 } +kind delete cluster --name kind || true # Build the docker image for vitess/lite using the local code docker build -f docker/lite/Dockerfile -t vitess/lite:pr . # Build the docker image for vitess/vtadmin using the local code -docker build -f docker/binaries/vtadmin/Dockerfile --build-arg VT_BASE_VER=pr -t vitess/vtadmin:pr . +docker build -f docker/binaries/vtadmin/Dockerfile --build-arg VT_BASE_VER=pr -t vitess/vtadmin:pr ./docker/binaries/vtadmin # Print the docker images available docker image ls diff --git a/tools/get_kubectl_kind.sh b/tools/get_kubectl_kind.sh index 57df414fdd8..169b120aaa0 100755 --- a/tools/get_kubectl_kind.sh +++ b/tools/get_kubectl_kind.sh @@ -12,7 +12,7 @@ source build.env mkdir -p "$VTROOT/bin" cd "$VTROOT/bin" -KUBE_VERSION="${KUBE_VERSION:-v1.21.1}" +KUBE_VERSION="${KUBE_VERSION:-v1.31.0}" KUBERNETES_RELEASE_URL="${KUBERNETES_RELEASE_URL:-https://dl.k8s.io}" # Download kubectl if needed. @@ -28,7 +28,7 @@ ln -sf "kubectl-${KUBE_VERSION}" kubectl if ! command -v kind &> /dev/null then echo "Downloading kind..." - curl -L https://kind.sigs.k8s.io/dl/v0.12.0/kind-linux-amd64 > "kind" + curl -L https://kind.sigs.k8s.io/dl/v0.22.0/kind-linux-amd64 > "kind" chmod +x "kind" echo "Installed kind" else diff --git a/tools/map-shard-for-value/Makefile b/tools/map-shard-for-value/Makefile new file mode 100644 index 00000000000..61bc88ac0ed --- /dev/null +++ b/tools/map-shard-for-value/Makefile @@ -0,0 +1,22 @@ +# 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. + +build: + go build map-shard-for-value.go + +test: + echo "1\n-1\n99" | go run map-shard-for-value.go --total_shards=4 --vindex=xxhash + +clean: + rm -f map-shard-for-value diff --git a/tools/map-shard-for-value/map-shard-for-value.go b/tools/map-shard-for-value/map-shard-for-value.go new file mode 100755 index 00000000000..18a092d1371 --- /dev/null +++ b/tools/map-shard-for-value/map-shard-for-value.go @@ -0,0 +1,207 @@ +/* +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 main + +import ( + "bufio" + "context" + "encoding/hex" + "fmt" + "log" + "os" + "strconv" + "strings" + + flag "github.com/spf13/pflag" + + "vitess.io/vitess/go/vt/topo" + + "vitess.io/vitess/go/sqltypes" + "vitess.io/vitess/go/vt/key" + "vitess.io/vitess/go/vt/proto/topodata" + "vitess.io/vitess/go/vt/vtgate/vindexes" +) + +/* + * This tool reads a list of values from stdin and prints the + * corresponding keyspace ID and shard for each value. It uses the given vindex + * and shard ranges to determine the shard. The vindex is expected to be a + * single-column vindex. The shard ranges are specified as a comma-separated + * list of key ranges, example "-80,80-". + * If you have uniformly distributed shards, you can specify the total number + * of shards using the -total_shards flag, and the tool will generate the shard ranges + * using the same logic as the Vitess operator does (using the key.GenerateShardRanges() function). + * + * Example usage: + * echo "1\n2\n3" | go run shard-from-id.go -vindex=hash -shards=-80,80- + * + * Currently tested only for integer values and hash/xxhash vindexes. + */ + +func mapShard(allShards []*topodata.ShardReference, ksid key.DestinationKeyspaceID) (string, error) { + foundShard := "" + addShard := func(shard string) error { + foundShard = shard + return nil + } + if err := ksid.Resolve(allShards, addShard); err != nil { + return "", fmt.Errorf("failed to resolve keyspace ID: %v:: %s", ksid.String(), err) + } + + if foundShard == "" { + return "", fmt.Errorf("no shard found for keyspace ID: %v", ksid) + } + return foundShard, nil +} + +func selectShard(vindex vindexes.Vindex, value sqltypes.Value, allShards []*topodata.ShardReference) (string, key.DestinationKeyspaceID, error) { + ctx := context.Background() + + destinations, err := vindexes.Map(ctx, vindex, nil, [][]sqltypes.Value{{value}}) + if err != nil { + return "", nil, fmt.Errorf("failed to map value to keyspace ID: %w", err) + } + + if len(destinations) != 1 { + return "", nil, fmt.Errorf("unexpected number of destinations: %d", len(destinations)) + } + + ksid, ok := destinations[0].(key.DestinationKeyspaceID) + if !ok { + return "", nil, fmt.Errorf("unexpected destination type: %T", destinations[0]) + } + + foundShard, err := mapShard(allShards, ksid) + if err != nil { + return "", nil, fmt.Errorf("failed to map shard, original value %v, keyspace id %s: %w", value, ksid, err) + } + return foundShard, ksid, nil +} + +func getValue(valueStr, valueType string) (sqltypes.Value, error) { + var value sqltypes.Value + + switch valueType { + case "int": + valueInt, err := strconv.ParseInt(valueStr, 10, 64) + if err != nil { + return value, fmt.Errorf("failed to parse int value: %w", err) + } + value = sqltypes.NewInt64(valueInt) + case "uint": + valueUint, err := strconv.ParseUint(valueStr, 10, 64) + if err != nil { + return value, fmt.Errorf("failed to parse uint value: %w", err) + } + value = sqltypes.NewUint64(valueUint) + case "string": + value = sqltypes.NewVarChar(valueStr) + default: + return value, fmt.Errorf("unsupported value type: %s", valueType) + } + + return value, nil +} + +func getShardMap(shardsCSV *string) []*topodata.ShardReference { + var allShards []*topodata.ShardReference + + for _, shard := range strings.Split(*shardsCSV, ",") { + _, keyRange, err := topo.ValidateShardName(shard) + if err != nil { + log.Fatalf("invalid shard range: %s", shard) + } + allShards = append(allShards, &topodata.ShardReference{ + Name: shard, + KeyRange: keyRange, + }) + } + return allShards +} + +type output struct { + Value string + KeyspaceID string + Shard string +} + +func processValues(scanner *bufio.Scanner, shardsCSV *string, vindexName string, valueType string) ([]output, error) { + allShards := getShardMap(shardsCSV) + + vindex, err := vindexes.CreateVindex(vindexName, vindexName, nil) + if err != nil { + return nil, fmt.Errorf("failed to create vindex: %v", err) + } + var outputs []output + for scanner.Scan() { + valueStr := scanner.Text() + if valueStr == "" { + continue + } + value, err := getValue(valueStr, valueType) + if err != nil { + return nil, fmt.Errorf("failed to get value for: %v, value_type %s:: %v", valueStr, valueType, err) + } + shard, ksid, err := selectShard(vindex, value, allShards) + if err != nil { + // ignore errors so that we can go ahead with the computation for other values + continue + } + outputs = append(outputs, output{Value: valueStr, KeyspaceID: hex.EncodeToString(ksid), Shard: shard}) + } + return outputs, nil +} + +func printOutput(outputs []output) { + fmt.Println("value,keyspaceID,shard") + for _, output := range outputs { + fmt.Printf("%s,%s,%s\n", output.Value, output.KeyspaceID, output.Shard) + } +} + +func main() { + // Explicitly configuring the logger since it was flaky in displaying logs locally without this. + log.SetOutput(os.Stderr) + log.SetFlags(log.LstdFlags) + log.SetPrefix("LOG: ") + + vindexName := flag.String("vindex", "xxhash", "name of the vindex") + shardsCSV := flag.String("shards", "", "comma-separated list of shard ranges") + totalShards := flag.Int("total_shards", 0, "total number of uniformly distributed shards") + valueType := flag.String("value_type", "int", "type of the value (int, uint, or string)") + flag.Parse() + + if *totalShards > 0 { + if *shardsCSV != "" { + log.Fatalf("cannot specify both total_shards and shards") + } + shardArr, err := key.GenerateShardRanges(*totalShards) + if err != nil { + log.Fatalf("failed to generate shard ranges: %v", err) + } + *shardsCSV = strings.Join(shardArr, ",") + } + if *shardsCSV == "" { + log.Fatal("shards or total_shards must be specified") + } + scanner := bufio.NewScanner(os.Stdin) + outputs, err := processValues(scanner, shardsCSV, *vindexName, *valueType) + if err != nil { + log.Fatalf("failed to process values: %v", err) + } + printOutput(outputs) +} diff --git a/tools/map-shard-for-value/map-shard-for-value.md b/tools/map-shard-for-value/map-shard-for-value.md new file mode 100644 index 00000000000..17daf7f5fe5 --- /dev/null +++ b/tools/map-shard-for-value/map-shard-for-value.md @@ -0,0 +1,47 @@ +## Map Shard for Value Tool + +### Overview + +The `map-shard-for-value` tool maps a given value to a specific shard. This tool helps in determining +which shard a particular value belongs to, based on the vindex algorithm and shard ranges. + +### Features +- + +- Allows specifying the vindex type (e.g., `hash`, `xxhash`). +- Allows specifying the shard list of (for uniformly distributed shard ranges) the total number of shards to generate. +- Designed as a _filter_: Reads input values from `stdin` and outputs the corresponding shard information, so it can be + used to map values from a file or another program. + +### Usage + +```sh +make build +``` + +```sh +echo "1\n-1\n99" | ./map-shard-for-value --total_shards=4 --vindex=xxhash +value,keyspaceID,shard +1,d46405367612b4b7,c0- +-1,d8e2a6a7c8c7623d,c0- +99,200533312244abca,-40 + +echo "1\n-1\n99" | ./map-shard-for-value --vindex=hash --shards="-80,80-" +value,keyspaceID,shard +1,166b40b44aba4bd6,-80 +-1,355550b2150e2451,-80 +99,2c40ad56f4593c47,-80 +``` + +#### Flags + +- `--vindex`: Specifies the name of the vindex to use (e.g., `hash`, `xxhash`) (default `xxhash`) + +One (and only one) of these is required: + +- `--shards`: Comma-separated list of shard ranges +- `--total_shards`: Total number of shards, only if shards are uniformly distributed + +Optional: +- `--value_type`: Type of the value to map, one of int, uint, string (default `int`) + diff --git a/tools/map-shard-for-value/map-shard-for-value_test.go b/tools/map-shard-for-value/map-shard-for-value_test.go new file mode 100644 index 00000000000..ca014818bb9 --- /dev/null +++ b/tools/map-shard-for-value/map-shard-for-value_test.go @@ -0,0 +1,90 @@ +/* +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 main + +import ( + "bufio" + "fmt" + "strings" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestProcess(t *testing.T) { + type testCase struct { + name string + shardsCSV string + vindexType string + values []int + valueType string + expected []output + } + testCases := []testCase{ + { + name: "hash,2 shards", + shardsCSV: "-80,80-", + vindexType: "hash", + values: []int{1, 99}, + valueType: "int", + expected: []output{ + { + Value: "1", + KeyspaceID: "166b40b44aba4bd6", + Shard: "-80", + }, + { + Value: "99", + KeyspaceID: "2c40ad56f4593c47", + Shard: "-80", + }, + }, + }, + { + name: "xxhash,4 shards", + shardsCSV: "-40,40-80,80-c0,c0-", + vindexType: "xxhash", + values: []int{1, 99}, + valueType: "int", + expected: []output{ + { + Value: "1", + KeyspaceID: "d46405367612b4b7", + Shard: "c0-", + }, + { + Value: "99", + KeyspaceID: "200533312244abca", + Shard: "-40", + }, + }, + }, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + var input strings.Builder + for _, num := range tc.values { + fmt.Fprintf(&input, "%d\n", num) + } + reader := strings.NewReader(input.String()) + scanner := bufio.NewScanner(reader) + got, err := processValues(scanner, &tc.shardsCSV, tc.vindexType, tc.valueType) + require.NoError(t, err) + require.EqualValues(t, tc.expected, got) + }) + } +} diff --git a/web/vtadmin/src/api/http.ts b/web/vtadmin/src/api/http.ts index 3f75330d240..674df961ef0 100644 --- a/web/vtadmin/src/api/http.ts +++ b/web/vtadmin/src/api/http.ts @@ -1068,3 +1068,41 @@ export const showVDiff = async ({ clusterID, request }: ShowVDiffParams) => { return vtadmin.VDiffShowResponse.create(result); }; + +export const fetchSchemaMigrations = async (request: vtadmin.IGetSchemaMigrationsRequest) => { + const { result } = await vtfetch(`/api/migrations/`, { + body: JSON.stringify(request), + method: 'post', + }); + + const err = vtadmin.GetSchemaMigrationsResponse.verify(result); + if (err) throw Error(err); + + return vtadmin.GetSchemaMigrationsResponse.create(result); +}; + +export interface ApplySchemaParams { + clusterID: string; + keyspace: string; + callerID: string; + sql: string; + request: vtctldata.IApplySchemaRequest; +} + +export const applySchema = async ({ clusterID, keyspace, callerID, sql, request }: ApplySchemaParams) => { + const body = { + sql, + caller_id: callerID, + request, + }; + + const { result } = await vtfetch(`/api/migration/${clusterID}/${keyspace}`, { + body: JSON.stringify(body), + method: 'post', + }); + + const err = vtctldata.ApplySchemaResponse.verify(result); + if (err) throw Error(err); + + return vtctldata.ApplySchemaResponse.create(result); +}; diff --git a/web/vtadmin/src/components/App.tsx b/web/vtadmin/src/components/App.tsx index ef27a35dc95..3bb41ea35f0 100644 --- a/web/vtadmin/src/components/App.tsx +++ b/web/vtadmin/src/components/App.tsx @@ -45,6 +45,8 @@ import { Transactions } from './routes/Transactions'; import { Transaction } from './routes/transaction/Transaction'; import { CreateReshard } from './routes/createWorkflow/CreateReshard'; import { CreateMaterialize } from './routes/createWorkflow/CreateMaterialize'; +import { SchemaMigrations } from './routes/SchemaMigrations'; +import { CreateSchemaMigration } from './routes/createSchemaMigration/CreateSchemaMigration'; export const App = () => { return ( @@ -140,6 +142,16 @@ export const App = () => { + + + + + {!isReadOnlyMode() && ( + + + + )} + diff --git a/web/vtadmin/src/components/NavRail.tsx b/web/vtadmin/src/components/NavRail.tsx index 9f9e1bf1681..b30cd165684 100644 --- a/web/vtadmin/src/components/NavRail.tsx +++ b/web/vtadmin/src/components/NavRail.tsx @@ -65,6 +65,9 @@ export const NavRail = () => {