diff --git a/.github/workflows/cluster_endtoend_24.yml b/.github/workflows/cluster_endtoend_mysql_server_vault.yml similarity index 93% rename from .github/workflows/cluster_endtoend_24.yml rename to .github/workflows/cluster_endtoend_mysql_server_vault.yml index 26339cce137..4dcf12b3acf 100644 --- a/.github/workflows/cluster_endtoend_24.yml +++ b/.github/workflows/cluster_endtoend_mysql_server_vault.yml @@ -1,9 +1,9 @@ # DO NOT MODIFY: THIS FILE IS GENERATED USING "make generate_ci_workflows" -name: Cluster (24) +name: Cluster (mysql_server_vault) on: [push, pull_request] concurrency: - group: format('{0}-{1}', ${{ github.ref }}, 'Cluster (24)') + group: format('{0}-{1}', ${{ github.ref }}, 'Cluster (mysql_server_vault)') cancel-in-progress: true env: @@ -13,7 +13,7 @@ env: jobs: build: - name: Run endtoend tests on Cluster (24) + name: Run endtoend tests on Cluster (mysql_server_vault) runs-on: ubuntu-18.04 steps: @@ -99,7 +99,7 @@ jobs: set -x # run the tests however you normally do, then produce a JUnit XML file - eatmydata -- go run test.go -docker=false -follow -shard 24 | tee -a output.txt | go-junit-report -set-exit-code > report.xml + eatmydata -- go run test.go -docker=false -follow -shard mysql_server_vault | tee -a output.txt | go-junit-report -set-exit-code > report.xml - name: Print test output and Record test result in launchable if: steps.changes.outputs.end_to_end == 'true' && always() diff --git a/.github/workflows/cluster_endtoend_resharding.yml b/.github/workflows/cluster_endtoend_resharding.yml deleted file mode 100644 index 4238c082cd9..00000000000 --- a/.github/workflows/cluster_endtoend_resharding.yml +++ /dev/null @@ -1,106 +0,0 @@ -# DO NOT MODIFY: THIS FILE IS GENERATED USING "make generate_ci_workflows" - -name: Cluster (resharding) -on: [push, pull_request] -concurrency: - group: format('{0}-{1}', ${{ github.ref }}, 'Cluster (resharding)') - cancel-in-progress: true - -env: - LAUNCHABLE_ORGANIZATION: "vitess" - LAUNCHABLE_WORKSPACE: "vitess-app" - GITHUB_PR_HEAD_SHA: "${{ github.event.pull_request.head.sha }}" - -jobs: - build: - name: Run endtoend tests on Cluster (resharding) - runs-on: ubuntu-18.04 - - steps: - - name: Check out code - uses: actions/checkout@v2 - - - name: Check for changes in relevant files - uses: frouioui/paths-filter@main - id: changes - with: - token: '' - filters: | - end_to_end: - - 'go/**/*.go' - - 'test.go' - - 'Makefile' - - 'build.env' - - 'go.[sumod]' - - 'proto/*.proto' - - 'tools/**' - - 'config/**' - - 'bootstrap.sh' - - '.github/workflows/**' - - - name: Set up Go - if: steps.changes.outputs.end_to_end == 'true' - uses: actions/setup-go@v2 - with: - go-version: 1.18.3 - - - name: Set up python - if: steps.changes.outputs.end_to_end == 'true' - uses: actions/setup-python@v2 - - - name: Tune the OS - if: steps.changes.outputs.end_to_end == 'true' - run: | - echo '1024 65535' | sudo tee -a /proc/sys/net/ipv4/ip_local_port_range - # Increase the asynchronous non-blocking I/O. More information at https://dev.mysql.com/doc/refman/5.7/en/innodb-parameters.html#sysvar_innodb_use_native_aio - echo "fs.aio-max-nr = 1048576" | sudo tee -a /etc/sysctl.conf - sudo sysctl -p /etc/sysctl.conf - - - name: Get dependencies - if: steps.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 service mysql stop - sudo service etcd stop - sudo ln -s /etc/apparmor.d/usr.sbin.mysqld /etc/apparmor.d/disable/ - sudo apparmor_parser -R /etc/apparmor.d/usr.sbin.mysqld - go mod download - - # install JUnit report formatter - go install github.com/jstemmer/go-junit-report@latest - - - name: Setup launchable dependencies - if: steps.changes.outputs.end_to_end == 'true' - run: | - # Get Launchable CLI installed. If you can, make it a part of the builder image to speed things up - pip3 install --user launchable~=1.0 > /dev/null - - # verify that launchable setup is all correct. - launchable verify || true - - # Tell Launchable about the build you are producing and testing - launchable record build --name "$GITHUB_RUN_ID" --source . - - - name: Run cluster endtoend test - if: steps.changes.outputs.end_to_end == 'true' - timeout-minutes: 30 - run: | - # We set the VTDATAROOT to the /tmp folder to reduce the file path of mysql.sock file - # which musn't be more than 107 characters long. - export VTDATAROOT="/tmp/" - source build.env - - set -x - - # run the tests however you normally do, then produce a JUnit XML file - eatmydata -- go run test.go -docker=false -follow -shard resharding | tee -a output.txt | go-junit-report -set-exit-code > report.xml - - - name: Print test output and Record test result in launchable - if: steps.changes.outputs.end_to_end == 'true' && always() - run: | - # send recorded tests to launchable - launchable record tests --build "$GITHUB_RUN_ID" go-test . || true - - # print test output - cat output.txt diff --git a/.github/workflows/cluster_endtoend_resharding_bytes.yml b/.github/workflows/cluster_endtoend_resharding_bytes.yml deleted file mode 100644 index 51ebdf65c33..00000000000 --- a/.github/workflows/cluster_endtoend_resharding_bytes.yml +++ /dev/null @@ -1,106 +0,0 @@ -# DO NOT MODIFY: THIS FILE IS GENERATED USING "make generate_ci_workflows" - -name: Cluster (resharding_bytes) -on: [push, pull_request] -concurrency: - group: format('{0}-{1}', ${{ github.ref }}, 'Cluster (resharding_bytes)') - cancel-in-progress: true - -env: - LAUNCHABLE_ORGANIZATION: "vitess" - LAUNCHABLE_WORKSPACE: "vitess-app" - GITHUB_PR_HEAD_SHA: "${{ github.event.pull_request.head.sha }}" - -jobs: - build: - name: Run endtoend tests on Cluster (resharding_bytes) - runs-on: ubuntu-18.04 - - steps: - - name: Check out code - uses: actions/checkout@v2 - - - name: Check for changes in relevant files - uses: frouioui/paths-filter@main - id: changes - with: - token: '' - filters: | - end_to_end: - - 'go/**/*.go' - - 'test.go' - - 'Makefile' - - 'build.env' - - 'go.[sumod]' - - 'proto/*.proto' - - 'tools/**' - - 'config/**' - - 'bootstrap.sh' - - '.github/workflows/**' - - - name: Set up Go - if: steps.changes.outputs.end_to_end == 'true' - uses: actions/setup-go@v2 - with: - go-version: 1.18.3 - - - name: Set up python - if: steps.changes.outputs.end_to_end == 'true' - uses: actions/setup-python@v2 - - - name: Tune the OS - if: steps.changes.outputs.end_to_end == 'true' - run: | - echo '1024 65535' | sudo tee -a /proc/sys/net/ipv4/ip_local_port_range - # Increase the asynchronous non-blocking I/O. More information at https://dev.mysql.com/doc/refman/5.7/en/innodb-parameters.html#sysvar_innodb_use_native_aio - echo "fs.aio-max-nr = 1048576" | sudo tee -a /etc/sysctl.conf - sudo sysctl -p /etc/sysctl.conf - - - name: Get dependencies - if: steps.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 service mysql stop - sudo service etcd stop - sudo ln -s /etc/apparmor.d/usr.sbin.mysqld /etc/apparmor.d/disable/ - sudo apparmor_parser -R /etc/apparmor.d/usr.sbin.mysqld - go mod download - - # install JUnit report formatter - go install github.com/jstemmer/go-junit-report@latest - - - name: Setup launchable dependencies - if: steps.changes.outputs.end_to_end == 'true' - run: | - # Get Launchable CLI installed. If you can, make it a part of the builder image to speed things up - pip3 install --user launchable~=1.0 > /dev/null - - # verify that launchable setup is all correct. - launchable verify || true - - # Tell Launchable about the build you are producing and testing - launchable record build --name "$GITHUB_RUN_ID" --source . - - - name: Run cluster endtoend test - if: steps.changes.outputs.end_to_end == 'true' - timeout-minutes: 30 - run: | - # We set the VTDATAROOT to the /tmp folder to reduce the file path of mysql.sock file - # which musn't be more than 107 characters long. - export VTDATAROOT="/tmp/" - source build.env - - set -x - - # run the tests however you normally do, then produce a JUnit XML file - eatmydata -- go run test.go -docker=false -follow -shard resharding_bytes | tee -a output.txt | go-junit-report -set-exit-code > report.xml - - - name: Print test output and Record test result in launchable - if: steps.changes.outputs.end_to_end == 'true' && always() - run: | - # send recorded tests to launchable - launchable record tests --build "$GITHUB_RUN_ID" go-test . || true - - # print test output - cat output.txt diff --git a/.github/workflows/cluster_endtoend_shardedrecovery_stress_verticalsplit_heavy.yml b/.github/workflows/cluster_endtoend_shardedrecovery_stress_verticalsplit_heavy.yml deleted file mode 100644 index be8fd919978..00000000000 --- a/.github/workflows/cluster_endtoend_shardedrecovery_stress_verticalsplit_heavy.yml +++ /dev/null @@ -1,125 +0,0 @@ -# DO NOT MODIFY: THIS FILE IS GENERATED USING "make generate_ci_workflows" - -name: Cluster (shardedrecovery_stress_verticalsplit_heavy) -on: [push, pull_request] -concurrency: - group: format('{0}-{1}', ${{ github.ref }}, 'Cluster (shardedrecovery_stress_verticalsplit_heavy)') - cancel-in-progress: true - -env: - LAUNCHABLE_ORGANIZATION: "vitess" - LAUNCHABLE_WORKSPACE: "vitess-app" - GITHUB_PR_HEAD_SHA: "${{ github.event.pull_request.head.sha }}" - -jobs: - build: - name: Run endtoend tests on Cluster (shardedrecovery_stress_verticalsplit_heavy) - runs-on: ubuntu-18.04 - - steps: - - name: Check out code - uses: actions/checkout@v2 - - - name: Check for changes in relevant files - uses: frouioui/paths-filter@main - id: changes - with: - token: '' - filters: | - end_to_end: - - 'go/**/*.go' - - 'test.go' - - 'Makefile' - - 'build.env' - - 'go.[sumod]' - - 'proto/*.proto' - - 'tools/**' - - 'config/**' - - 'bootstrap.sh' - - '.github/workflows/**' - - - name: Set up Go - if: steps.changes.outputs.end_to_end == 'true' - uses: actions/setup-go@v2 - with: - go-version: 1.18.3 - - - name: Set up python - if: steps.changes.outputs.end_to_end == 'true' - uses: actions/setup-python@v2 - - - name: Tune the OS - if: steps.changes.outputs.end_to_end == 'true' - run: | - echo '1024 65535' | sudo tee -a /proc/sys/net/ipv4/ip_local_port_range - # Increase the asynchronous non-blocking I/O. More information at https://dev.mysql.com/doc/refman/5.7/en/innodb-parameters.html#sysvar_innodb_use_native_aio - echo "fs.aio-max-nr = 1048576" | sudo tee -a /etc/sysctl.conf - sudo sysctl -p /etc/sysctl.conf - - - name: Get dependencies - if: steps.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 service mysql stop - sudo service etcd stop - sudo ln -s /etc/apparmor.d/usr.sbin.mysqld /etc/apparmor.d/disable/ - sudo apparmor_parser -R /etc/apparmor.d/usr.sbin.mysqld - go mod download - - # install JUnit report formatter - go install github.com/jstemmer/go-junit-report@latest - - - name: Setup launchable dependencies - if: steps.changes.outputs.end_to_end == 'true' - run: | - # Get Launchable CLI installed. If you can, make it a part of the builder image to speed things up - pip3 install --user launchable~=1.0 > /dev/null - - # verify that launchable setup is all correct. - launchable verify || true - - # Tell Launchable about the build you are producing and testing - launchable record build --name "$GITHUB_RUN_ID" --source . - - - name: Run cluster endtoend test - if: steps.changes.outputs.end_to_end == 'true' - timeout-minutes: 30 - run: | - # We set the VTDATAROOT to the /tmp folder to reduce the file path of mysql.sock file - # which musn't be more than 107 characters long. - export VTDATAROOT="/tmp/" - source build.env - - set -x - - # Increase our local ephemeral port range as we could exhaust this - sudo sysctl -w net.ipv4.ip_local_port_range="22768 61999" - # Increase our open file descriptor limit as we could hit this - ulimit -n 65536 - cat <<-EOF>>./config/mycnf/mysql57.cnf - innodb_buffer_pool_dump_at_shutdown=OFF - innodb_buffer_pool_load_at_startup=OFF - innodb_buffer_pool_size=64M - innodb_doublewrite=OFF - innodb_flush_log_at_trx_commit=0 - innodb_flush_method=O_DIRECT - innodb_numa_interleave=ON - innodb_adaptive_hash_index=OFF - sync_binlog=0 - sync_relay_log=0 - performance_schema=OFF - slow-query-log=OFF - EOF - - # run the tests however you normally do, then produce a JUnit XML file - eatmydata -- go run test.go -docker=false -follow -shard shardedrecovery_stress_verticalsplit_heavy | tee -a output.txt | go-junit-report -set-exit-code > report.xml - - - name: Print test output and Record test result in launchable - if: steps.changes.outputs.end_to_end == 'true' && always() - run: | - # send recorded tests to launchable - launchable record tests --build "$GITHUB_RUN_ID" go-test . || true - - # print test output - cat output.txt diff --git a/.github/workflows/cluster_endtoend_worker_vault_heavy.yml b/.github/workflows/cluster_endtoend_worker_vault_heavy.yml deleted file mode 100644 index 115dea868b9..00000000000 --- a/.github/workflows/cluster_endtoend_worker_vault_heavy.yml +++ /dev/null @@ -1,125 +0,0 @@ -# DO NOT MODIFY: THIS FILE IS GENERATED USING "make generate_ci_workflows" - -name: Cluster (worker_vault_heavy) -on: [push, pull_request] -concurrency: - group: format('{0}-{1}', ${{ github.ref }}, 'Cluster (worker_vault_heavy)') - cancel-in-progress: true - -env: - LAUNCHABLE_ORGANIZATION: "vitess" - LAUNCHABLE_WORKSPACE: "vitess-app" - GITHUB_PR_HEAD_SHA: "${{ github.event.pull_request.head.sha }}" - -jobs: - build: - name: Run endtoend tests on Cluster (worker_vault_heavy) - runs-on: ubuntu-18.04 - - steps: - - name: Check out code - uses: actions/checkout@v2 - - - name: Check for changes in relevant files - uses: frouioui/paths-filter@main - id: changes - with: - token: '' - filters: | - end_to_end: - - 'go/**/*.go' - - 'test.go' - - 'Makefile' - - 'build.env' - - 'go.[sumod]' - - 'proto/*.proto' - - 'tools/**' - - 'config/**' - - 'bootstrap.sh' - - '.github/workflows/**' - - - name: Set up Go - if: steps.changes.outputs.end_to_end == 'true' - uses: actions/setup-go@v2 - with: - go-version: 1.18.3 - - - name: Set up python - if: steps.changes.outputs.end_to_end == 'true' - uses: actions/setup-python@v2 - - - name: Tune the OS - if: steps.changes.outputs.end_to_end == 'true' - run: | - echo '1024 65535' | sudo tee -a /proc/sys/net/ipv4/ip_local_port_range - # Increase the asynchronous non-blocking I/O. More information at https://dev.mysql.com/doc/refman/5.7/en/innodb-parameters.html#sysvar_innodb_use_native_aio - echo "fs.aio-max-nr = 1048576" | sudo tee -a /etc/sysctl.conf - sudo sysctl -p /etc/sysctl.conf - - - name: Get dependencies - if: steps.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 service mysql stop - sudo service etcd stop - sudo ln -s /etc/apparmor.d/usr.sbin.mysqld /etc/apparmor.d/disable/ - sudo apparmor_parser -R /etc/apparmor.d/usr.sbin.mysqld - go mod download - - # install JUnit report formatter - go install github.com/jstemmer/go-junit-report@latest - - - name: Setup launchable dependencies - if: steps.changes.outputs.end_to_end == 'true' - run: | - # Get Launchable CLI installed. If you can, make it a part of the builder image to speed things up - pip3 install --user launchable~=1.0 > /dev/null - - # verify that launchable setup is all correct. - launchable verify || true - - # Tell Launchable about the build you are producing and testing - launchable record build --name "$GITHUB_RUN_ID" --source . - - - name: Run cluster endtoend test - if: steps.changes.outputs.end_to_end == 'true' - timeout-minutes: 30 - run: | - # We set the VTDATAROOT to the /tmp folder to reduce the file path of mysql.sock file - # which musn't be more than 107 characters long. - export VTDATAROOT="/tmp/" - source build.env - - set -x - - # Increase our local ephemeral port range as we could exhaust this - sudo sysctl -w net.ipv4.ip_local_port_range="22768 61999" - # Increase our open file descriptor limit as we could hit this - ulimit -n 65536 - cat <<-EOF>>./config/mycnf/mysql57.cnf - innodb_buffer_pool_dump_at_shutdown=OFF - innodb_buffer_pool_load_at_startup=OFF - innodb_buffer_pool_size=64M - innodb_doublewrite=OFF - innodb_flush_log_at_trx_commit=0 - innodb_flush_method=O_DIRECT - innodb_numa_interleave=ON - innodb_adaptive_hash_index=OFF - sync_binlog=0 - sync_relay_log=0 - performance_schema=OFF - slow-query-log=OFF - EOF - - # run the tests however you normally do, then produce a JUnit XML file - eatmydata -- go run test.go -docker=false -follow -shard worker_vault_heavy | tee -a output.txt | go-junit-report -set-exit-code > report.xml - - - name: Print test output and Record test result in launchable - if: steps.changes.outputs.end_to_end == 'true' && always() - run: | - # send recorded tests to launchable - launchable record tests --build "$GITHUB_RUN_ID" go-test . || true - - # print test output - cat output.txt diff --git a/.github/workflows/cluster_initial_sharding_multi.yml b/.github/workflows/cluster_initial_sharding_multi.yml deleted file mode 100644 index e40e93e0bb3..00000000000 --- a/.github/workflows/cluster_initial_sharding_multi.yml +++ /dev/null @@ -1,50 +0,0 @@ -name: cluster_initial_sharding_multi -on: [push, pull_request] -jobs: - - build: - name: cluster initial sharding multi - runs-on: ubuntu-latest - steps: - - name: Check out code - uses: actions/checkout@v2 - - - name: Check for changes in relevant files - uses: frouioui/paths-filter@main - id: changes - with: - token: '' - filters: | - end_to_end: - - 'go/**/*.go' - - 'test.go' - - 'Makefile' - - 'build.env' - - 'go.[sumod]' - - 'proto/*.proto' - - 'tools/**' - - 'config/**' - - 'bootstrap.sh' - - - name: Set up Go - if: steps.changes.outputs.end_to_end == 'true' - uses: actions/setup-go@v2 - with: - go-version: 1.18.3 - - - name: Tune the OS - if: steps.changes.outputs.end_to_end == 'true' - run: | - echo '1024 65535' | sudo tee -a /proc/sys/net/ipv4/ip_local_port_range - - # TEMPORARY WHILE GITHUB FIXES THIS https://github.com/actions/virtual-environments/issues/3185 - - name: Add the current IP address, long hostname and short hostname record to /etc/hosts file - if: steps.changes.outputs.end_to_end == 'true' - run: | - echo -e "$(ip addr show eth0 | grep "inet\b" | awk '{print $2}' | cut -d/ -f1)\t$(hostname -f) $(hostname -s)" | sudo tee -a /etc/hosts - # DON'T FORGET TO REMOVE CODE ABOVE WHEN ISSUE IS ADRESSED! - - - name: Run initial sharding multi - if: steps.changes.outputs.end_to_end == 'true' - run: | - go run test.go -print-log initial_sharding_multi diff --git a/.github/workflows/legacy_local_example.yml b/.github/workflows/legacy_local_example.yml deleted file mode 100644 index ac7b9dbb10f..00000000000 --- a/.github/workflows/legacy_local_example.yml +++ /dev/null @@ -1,88 +0,0 @@ -name: local_example -on: [push, pull_request] -jobs: - - build: - name: Legacy local example using ${{ matrix.topo }} on ${{ matrix.os }} - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [ubuntu-latest] - topo: [etcd,k8s] - - steps: - - name: Check out code - uses: actions/checkout@v2 - - - name: Check for changes in relevant files - uses: frouioui/paths-filter@main - id: changes - with: - token: '' - filters: | - examples: - - 'go/**/*.go' - - 'test.go' - - 'Makefile' - - 'build.env' - - 'go.[sumod]' - - 'proto/*.proto' - - 'tools/**' - - 'config/**' - - 'bootstrap.sh' - - 'examples/**' - - - name: Set up Go - if: steps.changes.outputs.examples == 'true' - uses: actions/setup-go@v2 - with: - go-version: 1.18.3 - - - name: Tune the OS - if: steps.changes.outputs.examples == 'true' - run: | - echo '1024 65535' | sudo tee -a /proc/sys/net/ipv4/ip_local_port_range - - - name: Get dependencies - if: steps.changes.outputs.examples == 'true' - run: | - if [ ${{matrix.os}} = "ubuntu-latest" ]; then - # Setup MySQL 8.0 - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.20-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 - if: steps.changes.outputs.examples == 'true' - run: | - make minimaltools - - - name: Build - if: steps.changes.outputs.examples == 'true' - run: | - make build - - - name: local_example - if: steps.changes.outputs.examples == 'true' - timeout-minutes: 30 - run: | - export TOPO=${{matrix.topo}} - if [ ${{matrix.os}} = "macos-latest" ]; then - export PATH="/usr/local/opt/mysql@5.7/bin:$PATH" - fi - # Make sure that testing is entirely non-reliant on config - mv config config-moved - eatmydata -- go run test.go -print-log -follow -retry=1 legacy_local_example diff --git a/Makefile b/Makefile index 13a345eac1b..91116e3e6dd 100644 --- a/Makefile +++ b/Makefile @@ -107,21 +107,21 @@ endif install: build # binaries mkdir -p "$${PREFIX}/bin" - cp "$${VTROOT}/bin/"{mysqlctl,mysqlctld,vtorc,vtadmin,vtctld,vtctlclient,vtctldclient,vtgate,vttablet,vtworker,vtbackup} "$${PREFIX}/bin/" + cp "$${VTROOT}/bin/"{mysqlctl,mysqlctld,vtorc,vtadmin,vtctld,vtctlclient,vtctldclient,vtgate,vttablet,vtbackup} "$${PREFIX}/bin/" # Will only work inside the docker bootstrap for now cross-install: cross-build # binaries mkdir -p "$${PREFIX}/bin" # Still no vtorc for cross-compile - cp "/go/bin/${GOOS}_${GOARCH}/"{mysqlctl,mysqlctld,vtadmin,vtctld,vtctlclient,vtctldclient,vtgate,vttablet,vtworker,vtbackup} "$${PREFIX}/bin/" + cp "/go/bin/${GOOS}_${GOARCH}/"{mysqlctl,mysqlctld,vtadmin,vtctld,vtctlclient,vtctldclient,vtgate,vttablet,vtbackup} "$${PREFIX}/bin/" # Install local install the binaries needed to run vitess locally # Usage: make install-local PREFIX=/path/to/install/root install-local: build # binaries mkdir -p "$${PREFIX}/bin" - cp "$${VTROOT}/bin/"{mysqlctl,mysqlctld,vtorc,vtadmin,vtctl,vtctld,vtctlclient,vtctldclient,vtgate,vttablet,vtworker,vtbackup} "$${PREFIX}/bin/" + cp "$${VTROOT}/bin/"{mysqlctl,mysqlctld,vtorc,vtadmin,vtctl,vtctld,vtctlclient,vtctldclient,vtgate,vttablet,vtbackup} "$${PREFIX}/bin/" # install copies the files needed to run test Vitess using vtcombo into the given directory tree. diff --git a/docker/k8s/Dockerfile b/docker/k8s/Dockerfile index 5f9c5ff672a..30ff33952bc 100644 --- a/docker/k8s/Dockerfile +++ b/docker/k8s/Dockerfile @@ -49,7 +49,6 @@ COPY --from=base /vt/bin/vtctl /vt/bin/ COPY --from=base /vt/bin/vtctlclient /vt/bin/ COPY --from=base /vt/bin/vtgate /vt/bin/ COPY --from=base /vt/bin/vttablet /vt/bin/ -COPY --from=base /vt/bin/vtworker /vt/bin/ COPY --from=base /vt/bin/vtbackup /vt/bin/ COPY --from=base /vt/bin/vtadmin /vt/bin/ diff --git a/docker/k8s/vtworker/Dockerfile b/docker/k8s/vtworker/Dockerfile deleted file mode 100644 index c2d7becb03d..00000000000 --- a/docker/k8s/vtworker/Dockerfile +++ /dev/null @@ -1,38 +0,0 @@ -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -ARG VT_BASE_VER=latest -ARG DEBIAN_VER=stable-slim - -FROM vitess/k8s:${VT_BASE_VER} AS k8s - -FROM debian:${DEBIAN_VER} - -# Set up Vitess environment (just enough to run pre-built Go binaries) -ENV VTROOT /vt - -# Prepare directory structure. -RUN mkdir -p /vt/bin && mkdir -p /vtdataroot - -# Copy certs to allow https calls -COPY --from=k8s /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt - -# Copy binaries -COPY --from=k8s /vt/bin/vtworker /vt/bin/ - -# add vitess user/group and add permissions -RUN groupadd -r --gid 2000 vitess && \ - useradd -r -g vitess --uid 1000 vitess && \ - chown -R vitess:vitess /vt && \ - chown -R vitess:vitess /vtdataroot diff --git a/docker/release.sh b/docker/release.sh index 37e9de2d10f..3ca6569387a 100755 --- a/docker/release.sh +++ b/docker/release.sh @@ -56,11 +56,6 @@ do docker push vitess/vtctld:$vt_base_version-$debian_version if [[ $debian_version == $default_debian_version ]]; then docker push vitess/vtctld:$vt_base_version; fi - docker build --platform linux/amd64 --build-arg VT_BASE_VER=$vt_base_version --build-arg DEBIAN_VER=$debian_version-slim -t vitess/vtworker:$vt_base_version-$debian_version k8s/vtworker - docker tag vitess/vtworker:$vt_base_version-$debian_version vitess/vtworker:$vt_base_version - docker push vitess/vtworker:$vt_base_version-$debian_version - if [[ $debian_version == $default_debian_version ]]; then docker push vitess/vtworker:$vt_base_version; fi - docker build --platform linux/amd64 --build-arg VT_BASE_VER=$vt_base_version --build-arg DEBIAN_VER=$debian_version-slim -t vitess/logrotate:$vt_base_version-$debian_version k8s/logrotate docker tag vitess/logrotate:$vt_base_version-$debian_version vitess/logrotate:$vt_base_version docker push vitess/logrotate:$vt_base_version-$debian_version diff --git a/examples/compose/docker-compose.yml b/examples/compose/docker-compose.yml index 1ffc4c55015..022d30ff15c 100644 --- a/examples/compose/docker-compose.yml +++ b/examples/compose/docker-compose.yml @@ -408,17 +408,4 @@ services: - "3306" volumes: - .:/script - vtwork: - command: - - sh - - -c - - '/vt/bin/vtworker -topo_implementation consul -topo_global_server_address consul1:8500 - -topo_global_root vitess/global -cell test -logtostderr=true -service_map ''grpc-vtworker'' - -port 8080 -grpc_port 15999 -use_v3_resharding_mode=true ' - depends_on: - - vtctld - image: vitess/lite:${VITESS_TAG:-latest} - ports: - - "8080" - - "15999" version: "2.1" diff --git a/examples/compose/vtcompose/docker-compose.test.yml b/examples/compose/vtcompose/docker-compose.test.yml index be97cfb4260..1ce97ca4848 100644 --- a/examples/compose/vtcompose/docker-compose.test.yml +++ b/examples/compose/vtcompose/docker-compose.test.yml @@ -301,18 +301,4 @@ services: - "3306" volumes: - .:/script - vtwork: - command: - - sh - - -c - - '$$VTROOT/bin/vtworker -topo_implementation consul -topo_global_server_address - consul1:8500 -topo_global_root vitess/global -cell test -logtostderr=true -service_map - ''grpc-vtworker'' -port 8080 -grpc_port 15999 -use_v3_resharding_mode=true -pid_file - $$VTDATAROOT/tmp/vtwork.pid ' - depends_on: - - vtctld - image: vitess/base - ports: - - 15100:8080 - - "15999" version: "2.1" diff --git a/examples/compose/vtcompose/vtcompose.go b/examples/compose/vtcompose/vtcompose.go index 4dd457d6cc7..d38b9959871 100644 --- a/examples/compose/vtcompose/vtcompose.go +++ b/examples/compose/vtcompose/vtcompose.go @@ -481,7 +481,6 @@ func applyDefaultDockerPatches( dockerComposeFile = applyInMemoryPatch(dockerComposeFile, generateVtctld(opts)) dockerComposeFile = applyInMemoryPatch(dockerComposeFile, generateVtgate(opts)) - dockerComposeFile = applyInMemoryPatch(dockerComposeFile, generateVtwork(opts)) dockerComposeFile = applyInMemoryPatch(dockerComposeFile, generateVreplication(dbInfo, opts)) dockerComposeFile = applyInMemoryPatch(dockerComposeFile, generateVtorc(dbInfo, keyspaceInfoMap, opts)) return dockerComposeFile @@ -724,29 +723,6 @@ func generateVtgate(opts vtOptions) string { `, opts.webPort, opts.gRpcPort, opts.mySqlPort, opts.topologyFlags, opts.cell) } -func generateVtwork(opts vtOptions) string { - return fmt.Sprintf(` -- op: add - path: /services/vtwork - value: - image: vitess/lite:${VITESS_TAG:-latest} - ports: - - "%[1]d" - - "%[2]d" - command: ["sh", "-c", "/vt/bin/vtworker \ - %[3]s \ - -cell %[4]s \ - -logtostderr=true \ - -service_map 'grpc-vtworker' \ - -port %[1]d \ - -grpc_port %[2]d \ - -use_v3_resharding_mode=true \ - "] - depends_on: - - vtctld -`, opts.webPort, opts.gRpcPort, opts.topologyFlags, opts.cell) -} - func generateVtorc(dbInfo externalDbInfo, keyspaceInfoMap map[string]keyspaceInfo, opts vtOptions) string { externalDb := "0" if dbInfo.dbName != "" { diff --git a/examples/legacy_local/101_initial_cluster.sh b/examples/legacy_local/101_initial_cluster.sh deleted file mode 100755 index 96e4b398d22..00000000000 --- a/examples/legacy_local/101_initial_cluster.sh +++ /dev/null @@ -1,53 +0,0 @@ -#!/bin/bash - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# this script brings up zookeeper and all the vitess components -# required for a single shard deployment. - -source ./env.sh - -# start topo server -if [ "${TOPO}" = "zk2" ]; then - CELL=zone1 ./scripts/zk-up.sh -elif [ "${TOPO}" = "k8s" ]; then - CELL=zone1 ./scripts/k3s-up.sh -else - CELL=zone1 ./scripts/etcd-up.sh -fi - -# start vtctld -CELL=zone1 ./scripts/vtctld-up.sh - -# start vttablets for keyspace commerce -for i in 100 101 102; do - CELL=zone1 TABLET_UID=$i ./scripts/mysqlctl-up.sh - CELL=zone1 KEYSPACE=commerce TABLET_UID=$i ./scripts/vttablet-up.sh -done - -# set the correct durability policy for the keyspace -vtctldclient --server localhost:15999 SetKeyspaceDurabilityPolicy --durability-policy=semi_sync commerce - -# set one of the replicas to primary -vtctlclient --server localhost:15999 InitShardPrimary -- --force commerce/0 zone1-100 - -# create the schema -vtctlclient --server localhost:15999 ApplySchema -- --sql-file create_commerce_schema.sql commerce - -# create the vschema -vtctlclient --server localhost:15999 ApplyVSchema -- --vschema_file vschema_commerce_initial.json commerce - -# start vtgate -CELL=zone1 ./scripts/vtgate-up.sh diff --git a/examples/legacy_local/201_customer_keyspace.sh b/examples/legacy_local/201_customer_keyspace.sh deleted file mode 100755 index 20749cfe61e..00000000000 --- a/examples/legacy_local/201_customer_keyspace.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# this script creates a new keyspace in preparation for vertical resharding - -vtctldclient --server localhost:15999 CreateKeyspace --served-from='primary:commerce,replica:commerce,rdonly:commerce' --durability-policy=semi_sync customer - diff --git a/examples/legacy_local/202_customer_tablets.sh b/examples/legacy_local/202_customer_tablets.sh deleted file mode 100755 index 925a6bca1e9..00000000000 --- a/examples/legacy_local/202_customer_tablets.sh +++ /dev/null @@ -1,32 +0,0 @@ -#!/bin/bash - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# this script creates the tablets and initializes them for vertical -# resharding it also splits the vschema between the two keyspaces -# old (commerce) and new (customer) - -source ./env.sh - -for i in 200 201 202; do - CELL=zone1 TABLET_UID=$i ./scripts/mysqlctl-up.sh - CELL=zone1 KEYSPACE=customer TABLET_UID=$i ./scripts/vttablet-up.sh -done - -vtctlclient --server localhost:15999 InitShardPrimary -- --force customer/0 zone1-200 -vtctlclient --server localhost:15999 CopySchemaShard -- --tables customer,corder commerce/0 customer/0 -vtctlclient --server localhost:15999 ApplyVSchema -- --vschema_file vschema_commerce_vsplit.json commerce -vtctlclient --server localhost:15999 ApplyVSchema -- --vschema_file vschema_customer_vsplit.json customer - diff --git a/examples/legacy_local/203_vertical_split.sh b/examples/legacy_local/203_vertical_split.sh deleted file mode 100755 index 1a00def3e3e..00000000000 --- a/examples/legacy_local/203_vertical_split.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/bash - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# this script copies over all the data from commerce keyspace to -# customer keyspace for the customer and corder tables - -source ./env.sh - -vtworker \ - $TOPOLOGY_FLAGS \ - --cell zone1 \ - --log_dir "$VTDATAROOT"/tmp \ - --alsologtostderr \ - --use_v3_resharding_mode \ - VerticalSplitClone -- --min_healthy_tablets=1 --tables=customer,corder customer/0 - diff --git a/examples/legacy_local/204_vertical_migrate_replicas.sh b/examples/legacy_local/204_vertical_migrate_replicas.sh deleted file mode 100755 index 47129f9459f..00000000000 --- a/examples/legacy_local/204_vertical_migrate_replicas.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/bash - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# this script migrates traffic for the new customer keyspace to the new -# tablets of types rdonly and replica - -vtctlclient --server localhost:15999 MigrateServedFrom customer/0 rdonly -vtctlclient --server localhost:15999 MigrateServedFrom customer/0 replica - diff --git a/examples/legacy_local/205_vertical_migrate_master.sh b/examples/legacy_local/205_vertical_migrate_master.sh deleted file mode 100755 index a28f9d1d565..00000000000 --- a/examples/legacy_local/205_vertical_migrate_master.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# this script migrates primary traffic for the customer keyspace to the -# new primary tablet - -vtctlclient --server localhost:15999 MigrateServedFrom customer/0 primary diff --git a/examples/legacy_local/206_clean_commerce.sh b/examples/legacy_local/206_clean_commerce.sh deleted file mode 100755 index 4b8e920c259..00000000000 --- a/examples/legacy_local/206_clean_commerce.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# this script removes the customer and corder tables from the commerce -# keyspace - -vtctlclient --server localhost:15999 ApplySchema -- --sql-file drop_commerce_tables.sql commerce -vtctlclient --server localhost:15999 SetShardTabletControl -- --denied_tables=customer,corder --remove commerce/0 rdonly -vtctlclient --server localhost:15999 SetShardTabletControl -- --denied_tables=customer,corder --remove commerce/0 replica -vtctlclient --server localhost:15999 SetShardTabletControl -- --denied_tables=customer,corder --remove commerce/0 primary diff --git a/examples/legacy_local/301_customer_sharded.sh b/examples/legacy_local/301_customer_sharded.sh deleted file mode 100755 index 76e6e6e9b96..00000000000 --- a/examples/legacy_local/301_customer_sharded.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/bash - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# this script creates vitess sequences for the auto_increment fields -# and alters the fields to no longer be auto_increment in preparation -# for horizontal sharding -# it also changes the customer vschema from unsharded to sharded and -# sets up the necessary vindexes - -vtctlclient --server localhost:15999 ApplySchema -- --sql-file create_commerce_seq.sql commerce -vtctlclient --server localhost:15999 ApplyVSchema -- --vschema_file vschema_commerce_seq.json commerce -vtctlclient --server localhost:15999 ApplySchema -- --sql-file create_customer_sharded.sql customer -vtctlclient --server localhost:15999 ApplyVSchema -- --vschema_file vschema_customer_sharded.json customer diff --git a/examples/legacy_local/302_new_shards.sh b/examples/legacy_local/302_new_shards.sh deleted file mode 100755 index 3a42725a223..00000000000 --- a/examples/legacy_local/302_new_shards.sh +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/bash - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# this script brings up new tablets for the two new shards that we will -# be creating in the customer keyspace and copies the schema - -source ./env.sh - -for i in 300 301 302; do - CELL=zone1 TABLET_UID=$i ./scripts/mysqlctl-up.sh - SHARD=-80 CELL=zone1 KEYSPACE=customer TABLET_UID=$i ./scripts/vttablet-up.sh -done - -for i in 400 401 402; do - CELL=zone1 TABLET_UID=$i ./scripts/mysqlctl-up.sh - SHARD=80- CELL=zone1 KEYSPACE=customer TABLET_UID=$i ./scripts/vttablet-up.sh -done - -vtctlclient --server localhost:15999 InitShardPrimary -- --force customer/-80 zone1-300 -vtctlclient --server localhost:15999 InitShardPrimary -- --force customer/80- zone1-400 -vtctlclient --server localhost:15999 CopySchemaShard customer/0 customer/-80 -vtctlclient --server localhost:15999 CopySchemaShard customer/0 customer/80- diff --git a/examples/legacy_local/303_horizontal_split.sh b/examples/legacy_local/303_horizontal_split.sh deleted file mode 100755 index 70b8f42248e..00000000000 --- a/examples/legacy_local/303_horizontal_split.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/bash - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# this script copies the data from customer/0 to customer/-80 and customer/80- -# each row will be copied to exactly one shard based on the vindex value - -source ./env.sh - -vtworker \ - $TOPOLOGY_FLAGS \ - --cell zone1 \ - --log_dir "$VTDATAROOT"/tmp \ - --alsologtostderr \ - --use_v3_resharding_mode \ - SplitClone -- --min_healthy_rdonly_tablets=1 customer/0 diff --git a/examples/legacy_local/304_migrate_replicas.sh b/examples/legacy_local/304_migrate_replicas.sh deleted file mode 100755 index 1da29bd4153..00000000000 --- a/examples/legacy_local/304_migrate_replicas.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# this script migrates traffic for the rdonly and replica tablets - -vtctlclient --server localhost:15999 MigrateServedTypes customer/0 rdonly -vtctlclient --server localhost:15999 MigrateServedTypes customer/0 replica diff --git a/examples/legacy_local/305_migrate_master.sh b/examples/legacy_local/305_migrate_master.sh deleted file mode 100755 index 64773e80110..00000000000 --- a/examples/legacy_local/305_migrate_master.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# this script migrates traffic for the primary tablet - -vtctlclient --server localhost:15999 MigrateServedTypes customer/0 primary -# data has been copied over to shards, and databases for the new shards are now available - diff --git a/examples/legacy_local/306_down_shard_0.sh b/examples/legacy_local/306_down_shard_0.sh deleted file mode 100755 index 25c23ab2111..00000000000 --- a/examples/legacy_local/306_down_shard_0.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/bash - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# this script brings down the tablets for customer/0 keyspace - -for i in 200 201 202; do - CELL=zone1 TABLET_UID=$i ./scripts/vttablet-down.sh - CELL=zone1 TABLET_UID=$i ./scripts/mysqlctl-down.sh -done - diff --git a/examples/legacy_local/307_delete_shard_0.sh b/examples/legacy_local/307_delete_shard_0.sh deleted file mode 100755 index c045dc7fcfd..00000000000 --- a/examples/legacy_local/307_delete_shard_0.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# this script deletes the old shard 0 which has been replaced by 2 shards - -vtctlclient --server localhost:15999 DeleteShard -- --recursive customer/0 diff --git a/examples/legacy_local/401_teardown.sh b/examples/legacy_local/401_teardown.sh deleted file mode 100755 index dbb9406866d..00000000000 --- a/examples/legacy_local/401_teardown.sh +++ /dev/null @@ -1,61 +0,0 @@ -#!/bin/bash - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# We should not assume that any of the steps have been executed. -# This makes it possible for a user to cleanup at any point. - -source ./env.sh - -./scripts/vtgate-down.sh - -for tablet in 100 200 300 400; do - if vtctlclient --server localhost:15999 GetTablet zone1-$tablet >/dev/null 2>&1 ; then - # The zero tablet is up. Try to shutdown 0-2 tablet + mysqlctl - for i in 0 1 2; do - uid=$[$tablet + $i] - CELL=zone1 TABLET_UID=$uid ./scripts/vttablet-down.sh - CELL=zone1 TABLET_UID=$uid ./scripts/mysqlctl-down.sh - done - fi -done - -./scripts/vtctld-down.sh - -if [ "${TOPO}" = "zk2" ]; then - CELL=zone1 ./scripts/zk-down.sh -elif [ "${TOPO}" = "k8s" ]; then - CELL=zone1 ./scripts/k3s-down.sh -else - CELL=zone1 ./scripts/etcd-down.sh -fi - -# pedantic check: grep for any remaining processes - -if [ ! -z "$VTDATAROOT" ]; then - - if pgrep -f -l "$VTDATAROOT" > /dev/null; then - echo "ERROR: Stale processes detected! It is recommended to manuallly kill them:" - pgrep -f -l "$VTDATAROOT" - else - echo "All good! It looks like every process has shut down" - fi - - # shellcheck disable=SC2086 - rm -r ${VTDATAROOT:?}/* - -fi - -disown -a diff --git a/examples/legacy_local/README.md b/examples/legacy_local/README.md deleted file mode 100644 index 245dbd4bb9e..00000000000 --- a/examples/legacy_local/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# Local Vitess Cluster - -This directory contains example scripts to bring up a Vitess cluster on your -local machine, which may be useful for experimentation. These scripts can -also serve as a starting point for configuring Vitess into your preferred -deployment strategy or toolset. - -See the [Run Vitess Locally](https://vitess.io/docs/tutorials/local/) -tutorial ("Start a Vitess cluster" section) for instructions on using these scripts. - diff --git a/examples/legacy_local/ceph_backup_config.json b/examples/legacy_local/ceph_backup_config.json deleted file mode 100644 index d71c6170699..00000000000 --- a/examples/legacy_local/ceph_backup_config.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "accessKey" : "AccessKey", - "secretKey" : "SecretKey", - "endPoint" : "URL", - "useSSL" : true -} diff --git a/examples/legacy_local/create_commerce_schema.sql b/examples/legacy_local/create_commerce_schema.sql deleted file mode 100644 index e62e7d2e8bf..00000000000 --- a/examples/legacy_local/create_commerce_schema.sql +++ /dev/null @@ -1,18 +0,0 @@ -create table product( - sku varbinary(128), - description varbinary(128), - price bigint, - primary key(sku) -) ENGINE=InnoDB; -create table customer( - customer_id bigint not null auto_increment, - email varbinary(128), - primary key(customer_id) -) ENGINE=InnoDB; -create table corder( - order_id bigint not null auto_increment, - customer_id bigint, - sku varbinary(128), - price bigint, - primary key(order_id) -) ENGINE=InnoDB; diff --git a/examples/legacy_local/create_commerce_seq.sql b/examples/legacy_local/create_commerce_seq.sql deleted file mode 100644 index b4e66c771b6..00000000000 --- a/examples/legacy_local/create_commerce_seq.sql +++ /dev/null @@ -1,4 +0,0 @@ -create table customer_seq(id int, next_id bigint, cache bigint, primary key(id)) comment 'vitess_sequence'; -insert into customer_seq(id, next_id, cache) values(0, 1000, 100); -create table order_seq(id int, next_id bigint, cache bigint, primary key(id)) comment 'vitess_sequence'; -insert into order_seq(id, next_id, cache) values(0, 1000, 100); diff --git a/examples/legacy_local/create_customer_sharded.sql b/examples/legacy_local/create_customer_sharded.sql deleted file mode 100644 index 9d3931c7c94..00000000000 --- a/examples/legacy_local/create_customer_sharded.sql +++ /dev/null @@ -1,2 +0,0 @@ -alter table customer change customer_id customer_id bigint not null; -alter table corder change order_id order_id bigint not null; diff --git a/examples/legacy_local/create_test_table.sql b/examples/legacy_local/create_test_table.sql deleted file mode 100644 index bfb24b766ad..00000000000 --- a/examples/legacy_local/create_test_table.sql +++ /dev/null @@ -1,7 +0,0 @@ -CREATE TABLE messages ( - page BIGINT(20) UNSIGNED, - time_created_ns BIGINT(20) UNSIGNED, - message VARCHAR(10000), - PRIMARY KEY (page, time_created_ns) -) ENGINE=InnoDB - diff --git a/examples/legacy_local/drop_commerce_tables.sql b/examples/legacy_local/drop_commerce_tables.sql deleted file mode 100644 index 3393ac8141a..00000000000 --- a/examples/legacy_local/drop_commerce_tables.sql +++ /dev/null @@ -1,2 +0,0 @@ -drop table customer; -drop table corder; diff --git a/examples/legacy_local/env.sh b/examples/legacy_local/env.sh deleted file mode 100644 index aa81c3ce3cd..00000000000 --- a/examples/legacy_local/env.sh +++ /dev/null @@ -1,72 +0,0 @@ -#!/bin/bash - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -set -e - -hostname=`hostname -f` -vtctld_web_port=15000 -export VTDATAROOT="${VTDATAROOT:-${PWD}/vtdataroot}" - -function fail() { - echo "ERROR: $1" - exit 1 -} - -if [[ $EUID -eq 0 ]]; then - fail "This script refuses to be run as root. Please switch to a regular user." -fi - -# mysqld might be in /usr/sbin which will not be in the default PATH -PATH="/usr/sbin:$PATH" -for binary in mysqld etcd etcdctl curl vtctlclient vttablet vtgate vtctld vtctl mysqlctl; do - command -v "$binary" > /dev/null || fail "${binary} is not installed in PATH. See https://vitess.io/docs/get-started/local/ for install instructions." -done; - -if [ "${TOPO}" = "zk2" ]; then - # Each ZooKeeper server needs a list of all servers in the quorum. - # Since we're running them all locally, we need to give them unique ports. - # In a real deployment, these should be on different machines, and their - # respective hostnames should be given. - zkcfg=(\ - "1@$hostname:28881:38881:21811" \ - "2@$hostname:28882:38882:21812" \ - "3@$hostname:28883:38883:21813" \ - ) - printf -v zkcfg ",%s" "${zkcfg[@]}" - zkcfg=${zkcfg:1} - - zkids='1 2 3' - - # Set topology environment parameters. - ZK_SERVER="localhost:21811,localhost:21812,localhost:21813" - # shellcheck disable=SC2034 - TOPOLOGY_FLAGS="--topo_implementation zk2 --topo_global_server_address ${ZK_SERVER} --topo_global_root /vitess/global" -elif [ "${TOPO}" = "k8s" ]; then - # Set topology environment parameters. - K8S_ADDR="localhost" - K8S_PORT="8443" - K8S_KUBECONFIG=$VTDATAROOT/tmp/k8s.kubeconfig - # shellcheck disable=SC2034 - TOPOLOGY_FLAGS="--topo_implementation k8s --topo_k8s_kubeconfig ${K8S_KUBECONFIG} --topo_global_server_address ${K8S_ADDR}:${K8S_PORT} --topo_global_root /vitess/global" -else - ETCD_SERVER="localhost:2379" - TOPOLOGY_FLAGS="--topo_implementation etcd2 --topo_global_server_address $ETCD_SERVER --topo_global_root /vitess/global" - - mkdir -p "${VTDATAROOT}/etcd" -fi - -# Create a tmp dir -mkdir -p "${VTDATAROOT}/tmp" diff --git a/examples/legacy_local/grpc_static_auth.json b/examples/legacy_local/grpc_static_auth.json deleted file mode 100644 index e6586acfb83..00000000000 --- a/examples/legacy_local/grpc_static_auth.json +++ /dev/null @@ -1,6 +0,0 @@ -[ - { - "Username": "vitess", - "Password": "vitess_password" - } -] diff --git a/examples/legacy_local/grpc_static_client_auth.json b/examples/legacy_local/grpc_static_client_auth.json deleted file mode 100644 index b808a87607f..00000000000 --- a/examples/legacy_local/grpc_static_client_auth.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "Username": "vitess", - "Password": "vitess_password" -} diff --git a/examples/legacy_local/mysql_auth_server_static_creds.json b/examples/legacy_local/mysql_auth_server_static_creds.json deleted file mode 100644 index cded6bc22c5..00000000000 --- a/examples/legacy_local/mysql_auth_server_static_creds.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "mysql_user": [ - { - "MysqlNativePassword": "*9E128DA0C64A6FCCCDCFBDD0FC0A2C967C6DB36F", - "Password": "mysql_password", - "UserData": "mysql_user" - } - ], - "mysql_user2": [ - { - "Password": "mysql_password", - "UserData": "mysql_user" - } - ], - "mysql_user3": [ - { - "MysqlNativePassword": "*9E128DA0C64A6FCCCDCFBDD0FC0A2C967C6DB36F", - "UserData": "mysql_user" - } - ], - - "vt_appdebug": [ - { - "Password": "vtappdebug_password", - "UserData": "vt_appdebug" - }, - { - "SourceHost": "localhost", - "Password": "", - "UserData": "vt_appdebug" - } - ] -} diff --git a/examples/legacy_local/scripts/etcd-down.sh b/examples/legacy_local/scripts/etcd-down.sh deleted file mode 100755 index 018af7432a3..00000000000 --- a/examples/legacy_local/scripts/etcd-down.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/bash - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# This is an example script that stops the etcd servers started by etcd-up.sh. - -source ./env.sh - -echo "Stopping etcd..." -kill -9 `cat $VTDATAROOT/tmp/etcd.pid` diff --git a/examples/legacy_local/scripts/etcd-up.sh b/examples/legacy_local/scripts/etcd-up.sh deleted file mode 100755 index 4d3d2607a43..00000000000 --- a/examples/legacy_local/scripts/etcd-up.sh +++ /dev/null @@ -1,51 +0,0 @@ -#!/bin/bash - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# This is an example script that creates a quorum of Etcd servers. - -source ./env.sh - -cell=${CELL:-'test'} -export ETCDCTL_API=2 - -# Check that etcd is not already running -curl "http://${ETCD_SERVER}" > /dev/null 2>&1 && fail "etcd is already running. Exiting." - -etcd --enable-v2=true --data-dir "${VTDATAROOT}/etcd/" --listen-client-urls "http://${ETCD_SERVER}" --advertise-client-urls "http://${ETCD_SERVER}" > "${VTDATAROOT}"/tmp/etcd.out 2>&1 & -PID=$! -echo $PID > "${VTDATAROOT}/tmp/etcd.pid" -sleep 5 - -echo "add /vitess/global" -etcdctl --endpoints "http://${ETCD_SERVER}" mkdir /vitess/global & - -echo "add /vitess/$cell" -etcdctl --endpoints "http://${ETCD_SERVER}" mkdir /vitess/$cell & - -# And also add the CellInfo description for the cell. -# If the node already exists, it's fine, means we used existing data. -echo "add $cell CellInfo" -set +e -# shellcheck disable=SC2086 -vtctl $TOPOLOGY_FLAGS AddCellInfo -- \ - --root /vitess/$cell \ - --server_address "${ETCD_SERVER}" \ - $cell -set -e - -echo "etcd start done..." - - diff --git a/examples/legacy_local/scripts/k3s-down.sh b/examples/legacy_local/scripts/k3s-down.sh deleted file mode 100755 index 590dc604e3e..00000000000 --- a/examples/legacy_local/scripts/k3s-down.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/bin/bash - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# This is an example script that stops the k3s server started by k3s-up.sh. - -set -e - -# shellcheck source=./env.sh -# shellcheck disable=SC1091 -source ./env.sh - -# Stop K3s server. -echo "Stopping k3s server..." - -pid=`cat $VTDATAROOT/tmp/k3s.pid` -echo "Stopping k3s..." -kill -9 $pid diff --git a/examples/legacy_local/scripts/k3s-up.sh b/examples/legacy_local/scripts/k3s-up.sh deleted file mode 100755 index 4268ce6c0c5..00000000000 --- a/examples/legacy_local/scripts/k3s-up.sh +++ /dev/null @@ -1,49 +0,0 @@ -#!/bin/bash - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# This is an example script that creates a Kubernetes api for topo use by running k3s - -set -e -cell=${CELL:-'test'} - -script_root=$(dirname "${BASH_SOURCE[0]}") - -# shellcheck source=./env.sh -# shellcheck disable=SC1091 -source ./env.sh - -k3s server --disable-agent --data-dir "${VTDATAROOT}/k3s/" --https-listen-port "${K8S_PORT}" --write-kubeconfig "${K8S_KUBECONFIG}" > "${VTDATAROOT}"/tmp/k3s.out 2>&1 & -PID=$! -echo $PID > "${VTDATAROOT}/tmp/k3s.pid" -disown -a -echo "Waiting for k3s server to start" -sleep 15 - -# Use k3s built-in kubectl with custom config -KUBECTL="k3s kubectl --kubeconfig=${K8S_KUBECONFIG}" - -# Create the CRD for vitesstopologynodes -$KUBECTL create -f ../../go/vt/topo/k8stopo/VitessTopoNodes-crd.yaml - -# Add the CellInfo description for the cell -set +e -echo "add $cell CellInfo" -vtctl $TOPOLOGY_FLAGS AddCellInfo -- \ - --root /vitess/$cell \ - $cell -set -e - -echo "k3s start done..." diff --git a/examples/legacy_local/scripts/mysqlctl-down.sh b/examples/legacy_local/scripts/mysqlctl-down.sh deleted file mode 100755 index 37b1d0c729b..00000000000 --- a/examples/legacy_local/scripts/mysqlctl-down.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/bash - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# This is an example script that stops the mysqld and vttablet instances -# created by vttablet-up.sh - -source ./env.sh - -cell=${CELL:-'test'} -uid=$TABLET_UID -printf -v alias '%s-%010d' $cell $uid -echo "Shutting down MySQL for tablet $alias..." - -mysqlctl --tablet_uid $TABLET_UID shutdown - diff --git a/examples/legacy_local/scripts/mysqlctl-up.sh b/examples/legacy_local/scripts/mysqlctl-up.sh deleted file mode 100755 index ae041cf951d..00000000000 --- a/examples/legacy_local/scripts/mysqlctl-up.sh +++ /dev/null @@ -1,42 +0,0 @@ -#!/bin/bash - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# This is an example script that creates a single shard vttablet deployment. - -source ./env.sh - -cell=${CELL:-'test'} -uid=$TABLET_UID -mysql_port=$[17000 + $uid] -printf -v alias '%s-%010d' $cell $uid -printf -v tablet_dir 'vt_%010d' $uid - -mkdir -p $VTDATAROOT/backups - -echo "Starting MySQL for tablet $alias..." -action="init" - -if [ -d $VTDATAROOT/$tablet_dir ]; then - echo "Resuming from existing vttablet dir:" - echo " $VTDATAROOT/$tablet_dir" - action='start' -fi - -mysqlctl \ - --log_dir $VTDATAROOT/tmp \ - --tablet_uid $uid \ - --mysql_port $mysql_port \ - $action diff --git a/examples/legacy_local/scripts/vtctld-down.sh b/examples/legacy_local/scripts/vtctld-down.sh deleted file mode 100755 index d96fa3b927f..00000000000 --- a/examples/legacy_local/scripts/vtctld-down.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/bash - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# This is an example script that stops vtctld. - -source ./env.sh - -echo "Stopping vtctld..." -kill -9 `cat $VTDATAROOT/tmp/vtctld.pid` diff --git a/examples/legacy_local/scripts/vtctld-up.sh b/examples/legacy_local/scripts/vtctld-up.sh deleted file mode 100755 index 7957bdec8ba..00000000000 --- a/examples/legacy_local/scripts/vtctld-up.sh +++ /dev/null @@ -1,38 +0,0 @@ -#!/bin/bash - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# This is an example script that starts vtctld. - -source ./env.sh - -cell=${CELL:-'test'} -grpc_port=15999 - -echo "Starting vtctld..." -# shellcheck disable=SC2086 -vtctld \ - $TOPOLOGY_FLAGS \ - --cell $cell \ - --workflow_manager_init \ - --workflow_manager_use_election \ - --service_map 'grpc-vtctl,grpc-vtctld' \ - --backup_storage_implementation file \ - --file_backup_storage_root $VTDATAROOT/backups \ - --log_dir $VTDATAROOT/tmp \ - --port $vtctld_web_port \ - --grpc_port $grpc_port \ - --pid_file $VTDATAROOT/tmp/vtctld.pid \ - > $VTDATAROOT/tmp/vtctld.out 2>&1 & diff --git a/examples/legacy_local/scripts/vtgate-down.sh b/examples/legacy_local/scripts/vtgate-down.sh deleted file mode 100755 index 9da0a7179df..00000000000 --- a/examples/legacy_local/scripts/vtgate-down.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# This is an example script that stops the instance started by vtgate-up.sh. - -source ./env.sh - -# Stop vtgate. -echo "Stopping vtgate..." -kill `cat $VTDATAROOT/tmp/vtgate.pid` diff --git a/examples/legacy_local/scripts/vtgate-up.sh b/examples/legacy_local/scripts/vtgate-up.sh deleted file mode 100755 index cb33e27839b..00000000000 --- a/examples/legacy_local/scripts/vtgate-up.sh +++ /dev/null @@ -1,57 +0,0 @@ -#!/bin/bash - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# This is an example script that starts a single vtgate. - -source ./env.sh - -cell=${CELL:-'test'} -web_port=15001 -grpc_port=15991 -mysql_server_port=15306 -mysql_server_socket_path="/tmp/mysql.sock" - -# Start vtgate. -# shellcheck disable=SC2086 -vtgate \ - $TOPOLOGY_FLAGS \ - --log_dir $VTDATAROOT/tmp \ - --log_queries_to_file $VTDATAROOT/tmp/vtgate_querylog.txt \ - --port $web_port \ - --grpc_port $grpc_port \ - --mysql_server_port $mysql_server_port \ - --mysql_server_socket_path $mysql_server_socket_path \ - --cell $cell \ - --cells_to_watch $cell \ - --tablet_types_to_wait PRIMARY,REPLICA \ - --service_map 'grpc-vtgateservice' \ - --pid_file $VTDATAROOT/tmp/vtgate.pid \ - --mysql_auth_server_impl none \ - > $VTDATAROOT/tmp/vtgate.out 2>&1 & - -# Block waiting for vtgate to be listening -# Not the same as healthy - -echo "Waiting for vtgate to be up..." -while true; do - curl -I "http://$hostname:$web_port/debug/status" >/dev/null 2>&1 && break - sleep 0.1 -done; -echo "vtgate is up!" - -echo "Access vtgate at http://$hostname:$web_port/debug/status" - -disown -a diff --git a/examples/legacy_local/scripts/vttablet-down.sh b/examples/legacy_local/scripts/vttablet-down.sh deleted file mode 100755 index 47b881b9793..00000000000 --- a/examples/legacy_local/scripts/vttablet-down.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/bin/bash - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# This is an example script that stops the mysqld and vttablet instances -# created by vttablet-up.sh - -source ./env.sh - -printf -v tablet_dir 'vt_%010d' $TABLET_UID -pid=`cat $VTDATAROOT/$tablet_dir/vttablet.pid` - -kill $pid - -# Wait for vttablet to die. -while ps -p $pid > /dev/null; do sleep 1; done - - diff --git a/examples/legacy_local/scripts/vttablet-up.sh b/examples/legacy_local/scripts/vttablet-up.sh deleted file mode 100755 index 327747c454f..00000000000 --- a/examples/legacy_local/scripts/vttablet-up.sh +++ /dev/null @@ -1,69 +0,0 @@ -#!/bin/bash - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -source ./env.sh - -cell=${CELL:-'test'} -keyspace=${KEYSPACE:-'test_keyspace'} -shard=${SHARD:-'0'} -uid=$TABLET_UID -mysql_port=$[17000 + $uid] -port=$[15000 + $uid] -grpc_port=$[16000 + $uid] -printf -v alias '%s-%010d' $cell $uid -printf -v tablet_dir 'vt_%010d' $uid -tablet_hostname='' -printf -v tablet_logfile 'vttablet_%010d_querylog.txt' $uid - -tablet_type=replica -if [[ "${uid: -1}" -gt 1 ]]; then - tablet_type=rdonly -fi - -echo "Starting vttablet for $alias..." -# shellcheck disable=SC2086 -vttablet \ - $TOPOLOGY_FLAGS \ - --log_dir $VTDATAROOT/tmp \ - --log_queries_to_file $VTDATAROOT/tmp/$tablet_logfile \ - --tablet-path $alias \ - --tablet_hostname "$tablet_hostname" \ - --init_keyspace $keyspace \ - --init_shard $shard \ - --init_tablet_type $tablet_type \ - --health_check_interval 5s \ - --enable_semi_sync \ - --enable_replication_reporter \ - --backup_storage_implementation file \ - --file_backup_storage_root $VTDATAROOT/backups \ - --restore_from_backup \ - --port $port \ - --grpc_port $grpc_port \ - --service_map 'grpc-queryservice,grpc-tabletmanager,grpc-updatestream' \ - --pid_file $VTDATAROOT/$tablet_dir/vttablet.pid \ - --vtctld_addr http://$hostname:$vtctld_web_port/ \ - > $VTDATAROOT/$tablet_dir/vttablet.out 2>&1 & - -# Block waiting for the tablet to be listening -# Not the same as healthy - -for i in $(seq 0 300); do - curl -I "http://$hostname:$port/debug/status" >/dev/null 2>&1 && break - sleep 0.1 -done - -# check one last time -curl -I "http://$hostname:$port/debug/status" || fail "tablet could not be started!" diff --git a/examples/legacy_local/scripts/zk-down.sh b/examples/legacy_local/scripts/zk-down.sh deleted file mode 100755 index 18dd7933bc9..00000000000 --- a/examples/legacy_local/scripts/zk-down.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/bash - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# This is an example script that stops the ZooKeeper servers started by zk-up.sh. - -source ./env.sh - -# Stop ZooKeeper servers. -echo "Stopping zk servers..." -for zkid in $zkids; do - zkctl -zk.myid $zkid -zk.cfg $zkcfg -log_dir $VTDATAROOT/tmp shutdown -done - diff --git a/examples/legacy_local/scripts/zk-up.sh b/examples/legacy_local/scripts/zk-up.sh deleted file mode 100755 index 0667d806838..00000000000 --- a/examples/legacy_local/scripts/zk-up.sh +++ /dev/null @@ -1,62 +0,0 @@ -#!/bin/bash - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# This is an example script that creates a quorum of ZooKeeper servers. - -source ./env.sh - -cell=${CELL:-'test'} - -# Start ZooKeeper servers. -# The "zkctl init" command won't return until the server is able to contact its -# peers, so we need to start them all in the background and then wait for them. -echo "Starting zk servers..." -for zkid in $zkids; do - action='init' - printf -v zkdir 'zk_%03d' $zkid - if [ -f $VTDATAROOT/$zkdir/myid ]; then - echo "Resuming from existing ZK data dir:" - echo " $VTDATAROOT/$zkdir" - action='start' - fi - zkctl -zk.myid $zkid -zk.cfg $zkcfg -log_dir $VTDATAROOT/tmp $action \ - > $VTDATAROOT/tmp/zkctl_$zkid.out 2>&1 & - pids[$zkid]=$! -done - -# Wait for all the zkctl commands to return. -echo "Waiting for zk servers to be ready..." - -for zkid in $zkids; do - if ! wait ${pids[$zkid]}; then - echo "ZK server number $zkid failed to start. See log:" - echo " $VTDATAROOT/tmp/zkctl_$zkid.out" - fi -done - -echo "Started zk servers." - -# Add the CellInfo description for the $CELL cell. -# If the node already exists, it's fine, means we used existing data. -set +e -# shellcheck disable=SC2086 -vtctl $TOPOLOGY_FLAGS AddCellInfo -- \ - --root /vitess/$cell \ - --server_address $ZK_SERVER \ - $cell -set -e - -echo "Configured zk servers." diff --git a/examples/legacy_local/topo-etcd2.sh b/examples/legacy_local/topo-etcd2.sh deleted file mode 100644 index c61543a806c..00000000000 --- a/examples/legacy_local/topo-etcd2.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# This is an example script that creates a single shard vttablet deployment. - -export TOPO='etcd2' - - diff --git a/examples/legacy_local/topo-k8s.sh b/examples/legacy_local/topo-k8s.sh deleted file mode 100644 index 92e14ba4d69..00000000000 --- a/examples/legacy_local/topo-k8s.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# This is an example script that creates a single shard vttablet deployment. - -export TOPO='k8s' - - diff --git a/examples/legacy_local/topo-zk2.sh b/examples/legacy_local/topo-zk2.sh deleted file mode 100644 index 29380949d8f..00000000000 --- a/examples/legacy_local/topo-zk2.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# This is an example script that creates a single shard vttablet deployment. - -export TOPO='zk2' - - diff --git a/examples/legacy_local/vschema.json b/examples/legacy_local/vschema.json deleted file mode 100644 index 17e5dedf0c8..00000000000 --- a/examples/legacy_local/vschema.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "sharded": true, - "vindexes": { - "hash": { - "type": "hash" - } - }, - "tables": { - "messages": { - "column_vindexes": [ - { - "column": "page", - "name": "hash" - } - ] - } - } -} diff --git a/examples/legacy_local/vschema_commerce_initial.json b/examples/legacy_local/vschema_commerce_initial.json deleted file mode 100644 index 0d1bae44428..00000000000 --- a/examples/legacy_local/vschema_commerce_initial.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "tables": { - "product": {}, - "customer": {}, - "corder": {} - } -} diff --git a/examples/legacy_local/vschema_commerce_seq.json b/examples/legacy_local/vschema_commerce_seq.json deleted file mode 100644 index 2e564fa6c96..00000000000 --- a/examples/legacy_local/vschema_commerce_seq.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "tables": { - "customer_seq": { - "type": "sequence" - }, - "order_seq": { - "type": "sequence" - }, - "product": {} - } -} diff --git a/examples/legacy_local/vschema_commerce_vsplit.json b/examples/legacy_local/vschema_commerce_vsplit.json deleted file mode 100644 index 30f34172e31..00000000000 --- a/examples/legacy_local/vschema_commerce_vsplit.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "tables": { - "product": {} - } -} diff --git a/examples/legacy_local/vschema_customer_sharded.json b/examples/legacy_local/vschema_customer_sharded.json deleted file mode 100644 index 3109e2a2f3c..00000000000 --- a/examples/legacy_local/vschema_customer_sharded.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "sharded": true, - "vindexes": { - "hash": { - "type": "hash" - } - }, - "tables": { - "customer": { - "column_vindexes": [ - { - "column": "customer_id", - "name": "hash" - } - ], - "auto_increment": { - "column": "customer_id", - "sequence": "customer_seq" - } - }, - "corder": { - "column_vindexes": [ - { - "column": "customer_id", - "name": "hash" - } - ], - "auto_increment": { - "column": "order_id", - "sequence": "order_seq" - } - } - } -} diff --git a/examples/legacy_local/vschema_customer_vsplit.json b/examples/legacy_local/vschema_customer_vsplit.json deleted file mode 100644 index b113a9adc9e..00000000000 --- a/examples/legacy_local/vschema_customer_vsplit.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "tables": { - "customer": {}, - "corder": {} - } -} diff --git a/go/cmd/vtctld/plugin_grpcvtworkerclient.go b/go/cmd/vtctld/plugin_grpcvtworkerclient.go deleted file mode 100644 index 74fa31ef4af..00000000000 --- a/go/cmd/vtctld/plugin_grpcvtworkerclient.go +++ /dev/null @@ -1,23 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -// Imports and registers the gRPC vtworker client. - -import ( - _ "vitess.io/vitess/go/vt/worker/grpcvtworkerclient" -) diff --git a/go/cmd/vtctldclient/command/keyspaces.go b/go/cmd/vtctldclient/command/keyspaces.go index f73bbcb6d57..4c887d42f23 100644 --- a/go/cmd/vtctldclient/command/keyspaces.go +++ b/go/cmd/vtctldclient/command/keyspaces.go @@ -26,7 +26,6 @@ import ( "vitess.io/vitess/go/cmd/vtctldclient/cli" "vitess.io/vitess/go/vt/logutil" "vitess.io/vitess/go/vt/topo" - "vitess.io/vitess/go/vt/topo/topoproto" topodatapb "vitess.io/vitess/go/vt/proto/topodata" vtctldatapb "vitess.io/vitess/go/vt/proto/vtctldata" @@ -107,24 +106,6 @@ SetKeyspaceDurabilityPolicy --durability-policy='semi_sync' customer`, Args: cobra.ExactArgs(1), RunE: commandSetKeyspaceDurabilityPolicy, } - // SetKeyspaceServedFrom makes a SetKeyspaceServedFrom gRPC call to a vtcltd. - SetKeyspaceServedFrom = &cobra.Command{ - Use: "SetKeyspaceServedFrom [--source ] [--remove] [--cells=] ", - Short: "Updates the ServedFromMap for a keyspace manually. This command is intended for emergency fixes. This command does not rebuild the serving graph.", - DisableFlagsInUseLine: true, - Args: cobra.ExactArgs(2), - RunE: commandSetKeyspaceServedFrom, - Deprecated: "and will soon be removed! Please use VReplication instead: https://vitess.io/docs/reference/vreplication", - } - // SetKeyspaceShardingInfo makes a SetKeyspaceShardingInfo gRPC call to a vtcltd. - SetKeyspaceShardingInfo = &cobra.Command{ - Use: "SetKeyspaceShardingInfo [--force] [ []]", - Short: "Updates the sharding information for a keyspace.", - DisableFlagsInUseLine: true, - Args: cobra.RangeArgs(1, 3), - RunE: commandSetKeyspaceShardingInfo, - Deprecated: "and will soon be removed! Please use VReplication instead: https://vitess.io/docs/reference/vreplication", - } // ValidateSchemaKeyspace makes a ValidateSchemaKeyspace gRPC call to a vtctld. ValidateSchemaKeyspace = &cobra.Command{ Use: "ValidateSchemaKeyspace [--exclude-tables=] [--include-views] [--skip-no-primary] [--include-vschema] ", @@ -365,49 +346,6 @@ func commandSetKeyspaceDurabilityPolicy(cmd *cobra.Command, args []string) error return nil } -var setKeyspaceServedFromOptions = struct { - Cells []string - SourceKeyspace string - Remove bool -}{} - -func commandSetKeyspaceServedFrom(cmd *cobra.Command, args []string) error { - keyspace := cmd.Flags().Arg(0) - tabletType, err := topoproto.ParseTabletType(cmd.Flags().Arg(1)) - if err != nil { - return err - } - - cli.FinishedParsing(cmd) - - resp, err := client.SetKeyspaceServedFrom(commandCtx, &vtctldatapb.SetKeyspaceServedFromRequest{ - Keyspace: keyspace, - TabletType: tabletType, - Cells: setKeyspaceServedFromOptions.Cells, - SourceKeyspace: setKeyspaceServedFromOptions.SourceKeyspace, - Remove: setKeyspaceServedFromOptions.Remove, - }) - if err != nil { - return err - } - - data, err := cli.MarshalJSON(resp) - if err != nil { - return err - } - - fmt.Printf("%s\n", data) - return nil -} - -var setKeyspaceShardingInfoOptions = struct { - Force bool -}{} - -func commandSetKeyspaceShardingInfo(cmd *cobra.Command, args []string) error { - return nil -} - var validateSchemaKeyspaceOptions = struct { ExcludeTables []string IncludeViews bool @@ -483,17 +421,9 @@ func init() { RemoveKeyspaceCell.Flags().BoolVarP(&removeKeyspaceCellOptions.Recursive, "recursive", "r", false, "Also delete all tablets in that cell beloning to the specified keyspace.") Root.AddCommand(RemoveKeyspaceCell) - SetKeyspaceServedFrom.Flags().StringSliceVarP(&setKeyspaceServedFromOptions.Cells, "cells", "c", nil, "Cells to affect (comma-separated).") - SetKeyspaceServedFrom.Flags().BoolVarP(&setKeyspaceServedFromOptions.Remove, "remove", "r", false, "If set, remove the ServedFrom record.") - SetKeyspaceServedFrom.Flags().StringVar(&setKeyspaceServedFromOptions.SourceKeyspace, "source", "", "Specifies the source keyspace name.") - Root.AddCommand(SetKeyspaceServedFrom) - SetKeyspaceDurabilityPolicy.Flags().StringVar(&setKeyspaceDurabilityPolicyOptions.DurabilityPolicy, "durability-policy", "none", "Type of durability to enforce for this keyspace. Default is none. Other values include 'semi_sync' and others as dictated by registered plugins.") Root.AddCommand(SetKeyspaceDurabilityPolicy) - SetKeyspaceShardingInfo.Flags().BoolVarP(&setKeyspaceShardingInfoOptions.Force, "force", "f", false, "Updates fields even if they are already set. Use caution before passing force to this command.") - Root.AddCommand(SetKeyspaceShardingInfo) - ValidateSchemaKeyspace.Flags().BoolVar(&validateSchemaKeyspaceOptions.IncludeViews, "include-views", false, "Includes views in compared schemas.") ValidateSchemaKeyspace.Flags().BoolVar(&validateSchemaKeyspaceOptions.IncludeVSchema, "include-vschema", false, "Includes VSchema validation in validation results.") ValidateSchemaKeyspace.Flags().BoolVar(&validateSchemaKeyspaceOptions.SkipNoPrimary, "skip-no-primary", false, "Skips validation on whether or not a primary exists in shards.") diff --git a/go/cmd/vtctldclient/command/shards.go b/go/cmd/vtctldclient/command/shards.go index cf586e2eb86..b670b2ce929 100644 --- a/go/cmd/vtctldclient/command/shards.go +++ b/go/cmd/vtctldclient/command/shards.go @@ -80,11 +80,10 @@ that shard.`, // SetShardTabletControl makes a SetShardTabletControl gRPC call to a vtctld. SetShardTabletControl = &cobra.Command{ Use: "SetShardTabletControl [--cells=c1,c2...] [--denied-tables=t1,t2,...] [--remove] [--disable-query-service[=0|false]] ", - Short: "Sets the TabletControl record for a shard and tablet type. Only use this for an emergency fix or after a finished MoveTables. The MigrateServedFrom and MigrateServedType commands set this record appropriately already.", + Short: "Sets the TabletControl record for a shard and tablet type. Only use this for an emergency fix or after a finished MoveTables.", Long: `Sets the TabletControl record for a shard and tablet type. -Only use this for an emergency fix or after a finished MoveTables. The MigrateServedFrom -and MigrateServedType commands set this record appropriately already. +Only use this for an emergency fix or after a finished MoveTables. Always specify the denied-tables flag for MoveTables, but never for Reshard operations. diff --git a/go/cmd/vtworker/plugin_consultopo.go b/go/cmd/vtworker/plugin_consultopo.go deleted file mode 100644 index 59d6774fdbc..00000000000 --- a/go/cmd/vtworker/plugin_consultopo.go +++ /dev/null @@ -1,23 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreedto 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 - -// This plugin imports consultopo to register the consul implementation of TopoServer. - -import ( - _ "vitess.io/vitess/go/vt/topo/consultopo" -) diff --git a/go/cmd/vtworker/plugin_etcd2topo.go b/go/cmd/vtworker/plugin_etcd2topo.go deleted file mode 100644 index d99ef51d4af..00000000000 --- a/go/cmd/vtworker/plugin_etcd2topo.go +++ /dev/null @@ -1,23 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -// This plugin imports etcd2topo to register the etcd2 implementation of TopoServer. - -import ( - _ "vitess.io/vitess/go/vt/topo/etcd2topo" -) diff --git a/go/cmd/vtworker/plugin_grpctabletconn.go b/go/cmd/vtworker/plugin_grpctabletconn.go deleted file mode 100644 index 08291a7c916..00000000000 --- a/go/cmd/vtworker/plugin_grpctabletconn.go +++ /dev/null @@ -1,23 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -// Imports and register the gRPC tabletconn client - -import ( - _ "vitess.io/vitess/go/vt/vttablet/grpctabletconn" -) diff --git a/go/cmd/vtworker/plugin_grpcthrottlerserver.go b/go/cmd/vtworker/plugin_grpcthrottlerserver.go deleted file mode 100644 index 40cce4bd51c..00000000000 --- a/go/cmd/vtworker/plugin_grpcthrottlerserver.go +++ /dev/null @@ -1,23 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -// Imports and register the gRPC throttler server. - -import ( - _ "vitess.io/vitess/go/vt/throttler/grpcthrottlerserver" -) diff --git a/go/cmd/vtworker/plugin_grpctmclient.go b/go/cmd/vtworker/plugin_grpctmclient.go deleted file mode 100644 index ce554da96df..00000000000 --- a/go/cmd/vtworker/plugin_grpctmclient.go +++ /dev/null @@ -1,23 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -// Imports and register the gRPC tabletmanager client - -import ( - _ "vitess.io/vitess/go/vt/vttablet/grpctmclient" -) diff --git a/go/cmd/vtworker/plugin_grpcvtworkerserver.go b/go/cmd/vtworker/plugin_grpcvtworkerserver.go deleted file mode 100644 index 20bfd13e9dc..00000000000 --- a/go/cmd/vtworker/plugin_grpcvtworkerserver.go +++ /dev/null @@ -1,30 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "vitess.io/vitess/go/vt/servenv" - "vitess.io/vitess/go/vt/worker/grpcvtworkerserver" -) - -func init() { - servenv.OnRun(func() { - if servenv.GRPCCheckServiceMap("vtworker") { - grpcvtworkerserver.StartServer(servenv.GRPCServer, wi) - } - }) -} diff --git a/go/cmd/vtworker/plugin_kubernetestopo.go b/go/cmd/vtworker/plugin_kubernetestopo.go deleted file mode 100644 index 671d0c8321f..00000000000 --- a/go/cmd/vtworker/plugin_kubernetestopo.go +++ /dev/null @@ -1,23 +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 main - -// This plugin imports k8stopo to register the kubernetes implementation of TopoServer. - -import ( - _ "vitess.io/vitess/go/vt/topo/k8stopo" -) diff --git a/go/cmd/vtworker/plugin_opentracing.go b/go/cmd/vtworker/plugin_opentracing.go deleted file mode 100644 index f023721be22..00000000000 --- a/go/cmd/vtworker/plugin_opentracing.go +++ /dev/null @@ -1,30 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "vitess.io/vitess/go/trace" - - "vitess.io/vitess/go/vt/servenv" -) - -func init() { - servenv.OnInit(func() { - closer := trace.StartTracing("vtworker") - servenv.OnClose(trace.LogErrorsWhenClosing(closer)) - }) -} diff --git a/go/cmd/vtworker/plugin_opentsdb.go b/go/cmd/vtworker/plugin_opentsdb.go deleted file mode 100644 index 1157cc18f32..00000000000 --- a/go/cmd/vtworker/plugin_opentsdb.go +++ /dev/null @@ -1,27 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -// This plugin imports opentsdb to register the opentsdb stats backend. - -import ( - "vitess.io/vitess/go/stats/opentsdb" -) - -func init() { - opentsdb.Init("vtworker") -} diff --git a/go/cmd/vtworker/plugin_prometheusbackend.go b/go/cmd/vtworker/plugin_prometheusbackend.go deleted file mode 100644 index 85efd13430e..00000000000 --- a/go/cmd/vtworker/plugin_prometheusbackend.go +++ /dev/null @@ -1,31 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -// This plugin imports Prometheus to allow for instrumentation -// with the Prometheus client library - -import ( - "vitess.io/vitess/go/stats/prometheusbackend" - "vitess.io/vitess/go/vt/servenv" -) - -func init() { - servenv.OnRun(func() { - prometheusbackend.Init("vtworker") - }) -} diff --git a/go/cmd/vtworker/plugin_zk2topo.go b/go/cmd/vtworker/plugin_zk2topo.go deleted file mode 100644 index ebf385ec1af..00000000000 --- a/go/cmd/vtworker/plugin_zk2topo.go +++ /dev/null @@ -1,23 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreedto 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 - -// Imports and register the zk2 TopologyServer - -import ( - _ "vitess.io/vitess/go/vt/topo/zk2topo" -) diff --git a/go/cmd/vtworker/vtworker.go b/go/cmd/vtworker/vtworker.go deleted file mode 100644 index 7e9b5ffd535..00000000000 --- a/go/cmd/vtworker/vtworker.go +++ /dev/null @@ -1,128 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -/* -vtworker is the main program to run a worker job. - -It has two modes: single command or interactive. -- in single command, it will start the job passed in from the command line, - and exit. -- in interactive mode, use a web browser to start an action. -*/ -package main - -import ( - "context" - "flag" - "io" - "os" - "time" - - "vitess.io/vitess/go/exit" - "vitess.io/vitess/go/vt/callerid" - "vitess.io/vitess/go/vt/log" - "vitess.io/vitess/go/vt/logutil" - "vitess.io/vitess/go/vt/servenv" - "vitess.io/vitess/go/vt/topo" - "vitess.io/vitess/go/vt/worker" - - // Include deprecation warnings for soon-to-be-unsupported flag invocations. - _flag "vitess.io/vitess/go/internal/flag" -) - -var ( - cell = flag.String("cell", "", "cell to pick servers from") - commandDisplayInterval = flag.Duration("command_display_interval", time.Second, "Interval between each status update when vtworker is executing a single command from the command line") - username = flag.String("username", "", "If set, value is set as immediate caller id in the request and used by vttablet for TableACL check") - _ = flag.String("durability_policy", "none", "type of durability to enforce. Default is none. Other values are dictated by registered plugins") -) - -func init() { - servenv.RegisterDefaultFlags() - - logger := logutil.NewConsoleLogger() - logger.Printf("*** This is a legacy sharding tool that will soon be removed! Please use the MoveTables or Reshard commands instead: https://vitess.io/docs/reference/vreplication/ ***\n") - flag.CommandLine.SetOutput(logutil.NewLoggerWriter(logger)) - _flag.SetUsage(flag.CommandLine, _flag.UsageOptions{ - Preface: func(w io.Writer) { - logger.Printf("Usage: %s [global parameters] command [command parameters]\n", os.Args[0]) - logger.Printf("\nThe global optional parameters are:\n") - }, - Epilogue: func(w io.Writer) { - logger.Printf("\nThe commands are listed below, sorted by group. Use '%s -h' for more help.\n\n", os.Args[0]) - worker.PrintAllCommands(logger) - }, - }) -} - -var ( - wi *worker.Instance -) - -func main() { - defer exit.Recover() - - _flag.Parse() - args := _flag.Args() - - servenv.Init() - defer servenv.Close() - - if *servenv.Version { - servenv.AppVersion.Print() - os.Exit(0) - } - - ts := topo.Open() - defer ts.Close() - - wi = worker.NewInstance(ts, *cell, *commandDisplayInterval) - wi.InstallSignalHandlers() - wi.InitStatusHandling() - - if len(args) == 0 { - // In interactive mode, initialize the web UI to choose a command. - wi.InitInteractiveMode() - } else { - // In single command mode, just run it. - ctx := context.Background() - - if *username != "" { - ctx = callerid.NewContext(ctx, - callerid.NewEffectiveCallerID("vtworker", "" /* component */, "" /* subComponent */), - callerid.NewImmediateCallerID(*username)) - } - - worker, done, err := wi.RunCommand(ctx, args, nil /*custom wrangler*/, true /*runFromCli*/) - if err != nil { - log.Error(err) - exit.Return(1) - } - // Run the subsequent, blocking wait asynchronously. - go func() { - if err := wi.WaitForCommand(worker, done); err != nil { - log.Error(err) - logutil.Flush() - // We cannot use exit.Return() here because we are in a different go routine now. - os.Exit(1) - } - logutil.Flush() - os.Exit(0) - }() - } - - servenv.RunDefault() -} diff --git a/go/cmd/vtworkerclient/plugin_grpcvtworkerclient.go b/go/cmd/vtworkerclient/plugin_grpcvtworkerclient.go deleted file mode 100644 index 74fa31ef4af..00000000000 --- a/go/cmd/vtworkerclient/plugin_grpcvtworkerclient.go +++ /dev/null @@ -1,23 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -// Imports and registers the gRPC vtworker client. - -import ( - _ "vitess.io/vitess/go/vt/worker/grpcvtworkerclient" -) diff --git a/go/cmd/vtworkerclient/vtworkerclient.go b/go/cmd/vtworkerclient/vtworkerclient.go deleted file mode 100644 index 351283bca15..00000000000 --- a/go/cmd/vtworkerclient/vtworkerclient.go +++ /dev/null @@ -1,64 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "context" - "flag" - "os" - "os/signal" - "syscall" - - "vitess.io/vitess/go/vt/log" - "vitess.io/vitess/go/vt/logutil" - "vitess.io/vitess/go/vt/worker/vtworkerclient" - - logutilpb "vitess.io/vitess/go/vt/proto/logutil" - - // Include deprecation warnings for soon-to-be-unsupported flag invocations. - _flag "vitess.io/vitess/go/internal/flag" -) - -var ( - server = flag.String("server", "", "server to use for connection") -) - -func main() { - _flag.Parse() - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - sigChan := make(chan os.Signal, 1) - signal.Notify(sigChan, syscall.SIGTERM, syscall.SIGINT) - go func() { - s := <-sigChan - log.Errorf("Trying to cancel current command after receiving signal: %v", s) - cancel() - }() - - logger := logutil.NewConsoleLogger() - - err := vtworkerclient.RunCommandAndWait( - ctx, *server, _flag.Args(), - func(e *logutilpb.Event) { - logutil.LogEvent(logger, e) - }) - if err != nil { - log.Error(err) - os.Exit(1) - } -} diff --git a/go/flags/endtoend/vtgate.txt b/go/flags/endtoend/vtgate.txt index 7fb70375f09..84f3ac429bd 100644 --- a/go/flags/endtoend/vtgate.txt +++ b/go/flags/endtoend/vtgate.txt @@ -278,7 +278,7 @@ Usage of vtgate: --security_policy string the name of a registered security policy to use for controlling access to URLs - empty means allow all for anyone (built-in policies: deny-all, read-only) --service_map value - comma separated list of services to enable (or disable if prefixed with '-') Example: grpc-vtworker + comma separated list of services to enable (or disable if prefixed with '-') Example: grpc-queryservice --sql-max-length-errors int truncate queries in error logs to the given length (default unlimited) --sql-max-length-ui int diff --git a/go/flags/endtoend/vttablet.txt b/go/flags/endtoend/vttablet.txt index f5e32839978..f711cae2617 100644 --- a/go/flags/endtoend/vttablet.txt +++ b/go/flags/endtoend/vttablet.txt @@ -55,8 +55,6 @@ Usage of vttablet: PITR restore parameter: Filename containing mTLS client private key for use in binlog server authentication. --binlog_ssl_server_name string PITR restore parameter: TLS server name (common name) to verify against for the binlog server we are connecting to (If not set: use the hostname or IP supplied in -binlog_host). - --binlog_use_v3_resharding_mode - (DEPRECATED) True if and only if the binlog streamer should use V3-style sharding, which doesn't require a preset sharding key column. (default true) --binlog_user string PITR restore parameter: username of binlog server. --builtinbackup_mysqld_timeout duration @@ -734,7 +732,7 @@ Usage of vttablet: --security_policy string the name of a registered security policy to use for controlling access to URLs - empty means allow all for anyone (built-in policies: deny-all, read-only) --service_map value - comma separated list of services to enable (or disable if prefixed with '-') Example: grpc-vtworker + comma separated list of services to enable (or disable if prefixed with '-') Example: grpc-queryservice --serving_state_grace_period duration how long to pause after broadcasting health to vtgate, before enforcing a new serving state --shard_sync_retry_delay duration diff --git a/go/test/endtoend/backup/transform/backup_transform_utils.go b/go/test/endtoend/backup/transform/backup_transform_utils.go index 62b52260a60..0661c65edbf 100644 --- a/go/test/endtoend/backup/transform/backup_transform_utils.go +++ b/go/test/endtoend/backup/transform/backup_transform_utils.go @@ -30,7 +30,6 @@ import ( "github.com/stretchr/testify/require" "vitess.io/vitess/go/test/endtoend/cluster" - "vitess.io/vitess/go/test/endtoend/sharding/initialsharding" "vitess.io/vitess/go/vt/log" ) @@ -87,11 +86,11 @@ func TestMainSetup(m *testing.M, useMysqlctld bool) { } shard := &localCluster.Keyspaces[0].Shards[0] // changing password for mysql user - dbCredentialFile = initialsharding.WriteDbCredentialToTmp(localCluster.TmpDirectory) + dbCredentialFile = cluster.WriteDbCredentialToTmp(localCluster.TmpDirectory) initDb, _ := os.ReadFile(path.Join(os.Getenv("VTROOT"), "/config/init_db.sql")) sql := string(initDb) newInitDBFile = path.Join(localCluster.TmpDirectory, "init_db_with_passwords.sql") - sql = sql + initialsharding.GetPasswordUpdateSQL(localCluster) + sql = sql + cluster.GetPasswordUpdateSQL(localCluster) os.WriteFile(newInitDBFile, []byte(sql), 0666) extraArgs := []string{"--db-credentials-file", dbCredentialFile} diff --git a/go/test/endtoend/backup/vtbackup/main_test.go b/go/test/endtoend/backup/vtbackup/main_test.go index 4dfe132f0c7..ce0720e77c4 100644 --- a/go/test/endtoend/backup/vtbackup/main_test.go +++ b/go/test/endtoend/backup/vtbackup/main_test.go @@ -25,7 +25,6 @@ import ( "testing" "vitess.io/vitess/go/test/endtoend/cluster" - "vitess.io/vitess/go/test/endtoend/sharding/initialsharding" "vitess.io/vitess/go/vt/log" ) @@ -87,11 +86,11 @@ func TestMain(m *testing.M) { // Create a new init_db.sql file that sets up passwords for all users. // Then we use a db-credentials-file with the passwords. - dbCredentialFile = initialsharding.WriteDbCredentialToTmp(localCluster.TmpDirectory) + dbCredentialFile = cluster.WriteDbCredentialToTmp(localCluster.TmpDirectory) initDb, _ := os.ReadFile(path.Join(os.Getenv("VTROOT"), "/config/init_db.sql")) sql := string(initDb) newInitDBFile = path.Join(localCluster.TmpDirectory, "init_db_with_passwords.sql") - sql = sql + initialsharding.GetPasswordUpdateSQL(localCluster) + sql = sql + cluster.GetPasswordUpdateSQL(localCluster) err = os.WriteFile(newInitDBFile, []byte(sql), 0666) if err != nil { return 1, err diff --git a/go/test/endtoend/backup/vtctlbackup/backup_utils.go b/go/test/endtoend/backup/vtctlbackup/backup_utils.go index f4e2e92a9ac..c1e629243f6 100644 --- a/go/test/endtoend/backup/vtctlbackup/backup_utils.go +++ b/go/test/endtoend/backup/vtctlbackup/backup_utils.go @@ -28,8 +28,6 @@ import ( "testing" "time" - "vitess.io/vitess/go/test/endtoend/sharding/initialsharding" - "vitess.io/vitess/go/vt/mysqlctl" "vitess.io/vitess/go/vt/proto/topodata" @@ -103,11 +101,11 @@ func LaunchCluster(setupType int, streamMode string, stripes int) (int, error) { } shard := &localCluster.Keyspaces[0].Shards[0] - dbCredentialFile = initialsharding.WriteDbCredentialToTmp(localCluster.TmpDirectory) + dbCredentialFile = cluster.WriteDbCredentialToTmp(localCluster.TmpDirectory) initDb, _ := os.ReadFile(path.Join(os.Getenv("VTROOT"), "/config/init_db.sql")) sql := string(initDb) newInitDBFile = path.Join(localCluster.TmpDirectory, "init_db_with_passwords.sql") - sql = sql + initialsharding.GetPasswordUpdateSQL(localCluster) + sql = sql + cluster.GetPasswordUpdateSQL(localCluster) err = os.WriteFile(newInitDBFile, []byte(sql), 0666) if err != nil { return 1, err diff --git a/go/test/endtoend/cellalias/cell_alias_test.go b/go/test/endtoend/cellalias/cell_alias_test.go index 81b2a0179cf..2a2ae3eaa20 100644 --- a/go/test/endtoend/cellalias/cell_alias_test.go +++ b/go/test/endtoend/cellalias/cell_alias_test.go @@ -34,7 +34,6 @@ import ( "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/test/endtoend/cluster" - "vitess.io/vitess/go/test/endtoend/sharding" "vitess.io/vitess/go/vt/proto/topodata" ) @@ -241,8 +240,8 @@ func TestAlias(t *testing.T) { expectedPartitions[topodata.TabletType_PRIMARY] = []string{shard1.Name, shard2.Name} expectedPartitions[topodata.TabletType_REPLICA] = []string{shard1.Name, shard2.Name} expectedPartitions[topodata.TabletType_RDONLY] = []string{shard1.Name, shard2.Name} - sharding.CheckSrvKeyspace(t, cell1, keyspaceName, expectedPartitions, *localCluster) - sharding.CheckSrvKeyspace(t, cell2, keyspaceName, expectedPartitions, *localCluster) + cluster.CheckSrvKeyspace(t, cell1, keyspaceName, expectedPartitions, *localCluster) + cluster.CheckSrvKeyspace(t, cell2, keyspaceName, expectedPartitions, *localCluster) // Adds alias so vtgate can route to replica/rdonly tablets that are not in the same cell, but same alias err = localCluster.VtctlclientProcess.ExecuteCommand("AddCellsAlias", "--", @@ -305,8 +304,8 @@ func TestAddAliasWhileVtgateUp(t *testing.T) { expectedPartitions[topodata.TabletType_PRIMARY] = []string{shard1.Name, shard2.Name} expectedPartitions[topodata.TabletType_REPLICA] = []string{shard1.Name, shard2.Name} expectedPartitions[topodata.TabletType_RDONLY] = []string{shard1.Name, shard2.Name} - sharding.CheckSrvKeyspace(t, cell1, keyspaceName, expectedPartitions, *localCluster) - sharding.CheckSrvKeyspace(t, cell2, keyspaceName, expectedPartitions, *localCluster) + cluster.CheckSrvKeyspace(t, cell1, keyspaceName, expectedPartitions, *localCluster) + cluster.CheckSrvKeyspace(t, cell2, keyspaceName, expectedPartitions, *localCluster) vtgateInstance := localCluster.NewVtgateInstance() vtgateInstance.CellsToWatch = allCells @@ -363,39 +362,39 @@ func testQueriesOnTabletType(t *testing.T, tabletType string, vtgateGrpcPort int } func insertInitialValues(t *testing.T) { - sharding.ExecuteOnTablet(t, - fmt.Sprintf(sharding.InsertTabletTemplateKsID, tableName, 1, "msg1", 1), + cluster.ExecuteOnTablet(t, + fmt.Sprintf(cluster.InsertTabletTemplateKsID, tableName, 1, "msg1", 1), *shard1Primary, keyspaceName, false) - sharding.ExecuteOnTablet(t, - fmt.Sprintf(sharding.InsertTabletTemplateKsID, tableName, 2, "msg2", 2), + cluster.ExecuteOnTablet(t, + fmt.Sprintf(cluster.InsertTabletTemplateKsID, tableName, 2, "msg2", 2), *shard1Primary, keyspaceName, false) - sharding.ExecuteOnTablet(t, - fmt.Sprintf(sharding.InsertTabletTemplateKsID, tableName, 4, "msg4", 4), + cluster.ExecuteOnTablet(t, + fmt.Sprintf(cluster.InsertTabletTemplateKsID, tableName, 4, "msg4", 4), *shard2Primary, keyspaceName, false) } func deleteInitialValues(t *testing.T) { - sharding.ExecuteOnTablet(t, + cluster.ExecuteOnTablet(t, fmt.Sprintf("delete from %s where id = %v", tableName, 1), *shard1Primary, keyspaceName, false) - sharding.ExecuteOnTablet(t, + cluster.ExecuteOnTablet(t, fmt.Sprintf("delete from %s where id = %v", tableName, 2), *shard1Primary, keyspaceName, false) - sharding.ExecuteOnTablet(t, + cluster.ExecuteOnTablet(t, fmt.Sprintf("delete from %s where id = %v", tableName, 4), *shard2Primary, keyspaceName, diff --git a/go/test/endtoend/cluster/cluster_process.go b/go/test/endtoend/cluster/cluster_process.go index d6e42211e43..dc51b7a8b01 100644 --- a/go/test/endtoend/cluster/cluster_process.go +++ b/go/test/endtoend/cluster/cluster_process.go @@ -87,7 +87,6 @@ type LocalProcessCluster struct { TopoProcess TopoProcess VtctldProcess VtctldProcess VtgateProcess VtgateProcess - VtworkerProcess VtworkerProcess VtbackupProcess VtbackupProcess VtorcProcesses []*VtorcProcess @@ -828,22 +827,6 @@ func (cluster *LocalProcessCluster) waitForMySQLProcessToExit(mysqlctlProcessLis wg.Wait() } -// StartVtworker starts a vtworker -func (cluster *LocalProcessCluster) StartVtworker(cell string, extraArgs ...string) error { - httpPort := cluster.GetAndReservePort() - grpcPort := cluster.GetAndReservePort() - log.Infof("Starting vtworker with http_port=%d, grpc_port=%d", httpPort, grpcPort) - cluster.VtworkerProcess = *VtworkerProcessInstance( - httpPort, - grpcPort, - cluster.TopoPort, - cluster.Hostname, - cluster.TmpDirectory) - cluster.VtworkerProcess.ExtraArgs = extraArgs - return cluster.VtworkerProcess.Setup(cell) - -} - // StartVtbackup starts a vtbackup func (cluster *LocalProcessCluster) StartVtbackup(newInitDBFile string, initalBackup bool, keyspace string, shard string, cell string, extraArgs ...string) error { diff --git a/go/test/endtoend/cluster/cluster_util.go b/go/test/endtoend/cluster/cluster_util.go index 8098c1623ee..eb6e8c2b2f4 100644 --- a/go/test/endtoend/cluster/cluster_util.go +++ b/go/test/endtoend/cluster/cluster_util.go @@ -20,24 +20,28 @@ import ( "context" "fmt" "os" + "path" + "reflect" "strings" "testing" "time" - replicationdatapb "vitess.io/vitess/go/vt/proto/replicationdata" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" + "vitess.io/vitess/go/json2" "vitess.io/vitess/go/mysql" - tabletpb "vitess.io/vitess/go/vt/proto/topodata" "vitess.io/vitess/go/vt/vtgate/vtgateconn" + + replicationdatapb "vitess.io/vitess/go/vt/proto/replicationdata" + topodatapb "vitess.io/vitess/go/vt/proto/topodata" tmc "vitess.io/vitess/go/vt/vttablet/grpctmclient" ) var ( - tmClient = tmc.NewClient() + tmClient = tmc.NewClient() + dbCredentialFile string + InsertTabletTemplateKsID = `insert into %s (id, msg) values (%d, '%s') /* id:%d */` ) // Restart restarts vttablet and mysql. @@ -183,10 +187,10 @@ func ResetTabletDirectory(tablet Vttablet) error { return tablet.MysqlctlProcess.Start() } -func getTablet(tabletGrpcPort int, hostname string) *tabletpb.Tablet { +func getTablet(tabletGrpcPort int, hostname string) *topodatapb.Tablet { portMap := make(map[string]int32) portMap["grpc"] = int32(tabletGrpcPort) - return &tabletpb.Tablet{Hostname: hostname, PortMap: portMap} + return &topodatapb.Tablet{Hostname: hostname, PortMap: portMap} } func filterResultForWarning(input string) string { @@ -292,3 +296,104 @@ func filterDoubleDashArgs(args []string, version int) (filtered []string) { return filtered } + +// WriteDbCredentialToTmp writes JSON formatted db credentials to the +// specified tmp directory. +func WriteDbCredentialToTmp(tmpDir string) string { + data := []byte(`{ + "vt_dba": ["VtDbaPass"], + "vt_app": ["VtAppPass"], + "vt_allprivs": ["VtAllprivsPass"], + "vt_repl": ["VtReplPass"], + "vt_filtered": ["VtFilteredPass"] + }`) + dbCredentialFile = path.Join(tmpDir, "db_credentials.json") + os.WriteFile(dbCredentialFile, data, 0666) + return dbCredentialFile +} + +// GetPasswordUpdateSQL returns the SQL for updating the users' passwords +// to the static creds used throughout tests. +func GetPasswordUpdateSQL(localCluster *LocalProcessCluster) string { + pwdChangeCmd := ` + # Set real passwords for all users. + UPDATE mysql.user SET %s = PASSWORD('RootPass') + WHERE User = 'root' AND Host = 'localhost'; + UPDATE mysql.user SET %s = PASSWORD('VtDbaPass') + WHERE User = 'vt_dba' AND Host = 'localhost'; + UPDATE mysql.user SET %s = PASSWORD('VtAppPass') + WHERE User = 'vt_app' AND Host = 'localhost'; + UPDATE mysql.user SET %s = PASSWORD('VtAllprivsPass') + WHERE User = 'vt_allprivs' AND Host = 'localhost'; + UPDATE mysql.user SET %s = PASSWORD('VtReplPass') + WHERE User = 'vt_repl' AND Host = '%%'; + UPDATE mysql.user SET %s = PASSWORD('VtFilteredPass') + WHERE User = 'vt_filtered' AND Host = 'localhost'; + FLUSH PRIVILEGES; + ` + pwdCol, _ := getPasswordField(localCluster) + return fmt.Sprintf(pwdChangeCmd, pwdCol, pwdCol, pwdCol, pwdCol, pwdCol, pwdCol) +} + +// getPasswordField determines which column is used for user passwords in this MySQL version. +func getPasswordField(localCluster *LocalProcessCluster) (pwdCol string, err error) { + tablet := &Vttablet{ + Type: "relpica", + TabletUID: 100, + MySQLPort: 15000, + MysqlctlProcess: *MysqlCtlProcessInstance(100, 15000, localCluster.TmpDirectory), + } + if err = tablet.MysqlctlProcess.Start(); err != nil { + return "", err + } + tablet.VttabletProcess = VttabletProcessInstance(tablet.HTTPPort, tablet.GrpcPort, tablet.TabletUID, "", "", "", 0, tablet.Type, localCluster.TopoPort, "", "", nil, false, localCluster.DefaultCharset) + result, err := tablet.VttabletProcess.QueryTablet("select password from mysql.user limit 0", "", false) + if err == nil && len(result.Rows) > 0 { + return "password", nil + } + tablet.MysqlctlProcess.Stop() + os.RemoveAll(path.Join(tablet.VttabletProcess.Directory)) + return "authentication_string", nil + +} + +// CheckSrvKeyspace confirms that the cell and keyspace contain the expected +// shard mappings. +func CheckSrvKeyspace(t *testing.T, cell string, ksname string, expectedPartition map[topodatapb.TabletType][]string, ci LocalProcessCluster) { + srvKeyspace := GetSrvKeyspace(t, cell, ksname, ci) + + currentPartition := map[topodatapb.TabletType][]string{} + + for _, partition := range srvKeyspace.Partitions { + currentPartition[partition.ServedType] = []string{} + for _, shardRef := range partition.ShardReferences { + currentPartition[partition.ServedType] = append(currentPartition[partition.ServedType], shardRef.Name) + } + } + + assert.True(t, reflect.DeepEqual(currentPartition, expectedPartition)) +} + +// GetSrvKeyspace returns the SrvKeyspace structure for the cell and keyspace. +func GetSrvKeyspace(t *testing.T, cell string, ksname string, ci LocalProcessCluster) *topodatapb.SrvKeyspace { + output, err := ci.VtctlclientProcess.ExecuteCommandWithOutput("GetSrvKeyspace", cell, ksname) + require.Nil(t, err) + var srvKeyspace topodatapb.SrvKeyspace + + err = json2.Unmarshal([]byte(output), &srvKeyspace) + require.Nil(t, err) + return &srvKeyspace +} + +// ExecuteOnTablet executes a query on the specified vttablet. +// It should always be called with a primary tablet for a keyspace/shard. +func ExecuteOnTablet(t *testing.T, query string, vttablet Vttablet, ks string, expectFail bool) { + _, _ = vttablet.VttabletProcess.QueryTablet("begin", ks, true) + _, err := vttablet.VttabletProcess.QueryTablet(query, ks, true) + if expectFail { + require.Error(t, err) + } else { + require.Nil(t, err) + } + _, _ = vttablet.VttabletProcess.QueryTablet("commit", ks, true) +} diff --git a/go/test/endtoend/cluster/vtctld_process.go b/go/test/endtoend/cluster/vtctld_process.go index 16e23ae109e..608909608d9 100644 --- a/go/test/endtoend/cluster/vtctld_process.go +++ b/go/test/endtoend/cluster/vtctld_process.go @@ -64,9 +64,6 @@ func (vtctld *VtctldProcess) Setup(cell string, extraArgs ...string) (err error) "--service_map", vtctld.ServiceMap, "--backup_storage_implementation", vtctld.BackupStorageImplementation, "--file_backup_storage_root", vtctld.FileBackupStorageRoot, - // hard-code these two soon-to-be deprecated drain values. - "--wait_for_drain_sleep_rdonly", "1s", - "--wait_for_drain_sleep_replica", "1s", "--log_dir", vtctld.LogDir, "--port", fmt.Sprintf("%d", vtctld.Port), "--grpc_port", fmt.Sprintf("%d", vtctld.GrpcPort), diff --git a/go/test/endtoend/cluster/vtgate_process.go b/go/test/endtoend/cluster/vtgate_process.go index b63a8cb6f5f..d460c387bbc 100644 --- a/go/test/endtoend/cluster/vtgate_process.go +++ b/go/test/endtoend/cluster/vtgate_process.go @@ -236,7 +236,7 @@ func VtgateProcessInstance( Binary: "vtgate", FileToLogQueries: path.Join(tmpDirectory, "/vtgate_querylog.txt"), Directory: os.Getenv("VTDATAROOT"), - ServiceMap: "grpc-tabletmanager,grpc-throttler,grpc-queryservice,grpc-updatestream,grpc-vtctl,grpc-vtworker,grpc-vtgateservice", + ServiceMap: "grpc-tabletmanager,grpc-throttler,grpc-queryservice,grpc-updatestream,grpc-vtctl,grpc-vtgateservice", LogDir: tmpDirectory, Port: port, GrpcPort: grpcPort, diff --git a/go/test/endtoend/cluster/vtworker_process.go b/go/test/endtoend/cluster/vtworker_process.go deleted file mode 100644 index bbd41e4b8b6..00000000000 --- a/go/test/endtoend/cluster/vtworker_process.go +++ /dev/null @@ -1,233 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package cluster - -import ( - "encoding/json" - "fmt" - "io" - "net/http" - "os" - "os/exec" - "strings" - "syscall" - "time" - - "vitess.io/vitess/go/vt/log" -) - -// VtworkerProcess is a generic handle for a running vtworker . -// It can be spawned manually -type VtworkerProcess struct { - Name string - Binary string - CommonArg VtctlProcess - ServiceMap string - LogDir string - Port int - GrpcPort int - VerifyURL string - Directory string - ExecuteRetryTime string - Cell string - Server string - CommandDisplayInterval string - ExtraArgs []string - - proc *exec.Cmd - exit chan error -} - -// Setup starts vtworker process with required arguements -func (vtworker *VtworkerProcess) Setup(cell string) (err error) { - - vtworker.proc = exec.Command( - vtworker.Binary, - "--log_dir", vtworker.LogDir, - "--port", fmt.Sprintf("%d", vtworker.Port), - "--executefetch_retry_time", vtworker.ExecuteRetryTime, - "--tablet_manager_protocol", "grpc", - "--tablet_protocol", "grpc", - "--topo_implementation", vtworker.CommonArg.TopoImplementation, - "--topo_global_server_address", vtworker.CommonArg.TopoGlobalAddress, - "--topo_global_root", vtworker.CommonArg.TopoGlobalRoot, - "--service_map", vtworker.ServiceMap, - "--grpc_port", fmt.Sprintf("%d", vtworker.GrpcPort), - "--cell", cell, - "--command_display_interval", "10ms", - ) - if *isCoverage { - vtworker.proc.Args = append(vtworker.proc.Args, "--test.coverprofile=vtworker.out", "--test.v") - } - vtworker.proc.Args = append(vtworker.proc.Args, vtworker.ExtraArgs...) - - vtworker.proc.Stderr = os.Stderr - vtworker.proc.Stdout = os.Stdout - - vtworker.proc.Env = append(vtworker.proc.Env, os.Environ()...) - - log.Infof("%v", strings.Join(vtworker.proc.Args, " ")) - - err = vtworker.proc.Start() - if err != nil { - return - } - - vtworker.exit = make(chan error) - go func() { - vtworker.exit <- vtworker.proc.Wait() - }() - - timeout := time.Now().Add(60 * time.Second) - for time.Now().Before(timeout) { - if vtworker.IsHealthy() { - return nil - } - select { - case err := <-vtworker.exit: - return fmt.Errorf("process '%s' exited prematurely (err: %s)", vtworker.Name, err) - default: - time.Sleep(300 * time.Millisecond) - } - } - - return fmt.Errorf("process '%s' timed out after 60s (err: %s)", vtworker.Name, <-vtworker.exit) -} - -// IsHealthy function checks if vtworker process is up and running -func (vtworker *VtworkerProcess) IsHealthy() bool { - resp, err := http.Get(vtworker.VerifyURL) - if err != nil { - return false - } - if resp.StatusCode == 200 { - return true - } - return false -} - -// TearDown shutdowns the running vtworker process -func (vtworker *VtworkerProcess) TearDown() error { - if vtworker.proc == nil || vtworker.exit == nil { - return nil - } - - // Attempt graceful shutdown with SIGTERM first - vtworker.proc.Process.Signal(syscall.SIGTERM) - - select { - case err := <-vtworker.exit: - vtworker.proc = nil - return err - - case <-time.After(10 * time.Second): - vtworker.proc.Process.Kill() - vtworker.proc = nil - return <-vtworker.exit - } -} - -// ExecuteCommand executes any vtworker command -func (vtworker *VtworkerProcess) ExecuteCommand(args ...string) (err error) { - args = append([]string{"--vtworker_client_protocol", "grpc", - "--server", vtworker.Server, "--log_dir", vtworker.LogDir, "--stderrthreshold", "info"}, args...) - if *isCoverage { - args = append([]string{"--test.coverprofile=" + getCoveragePath("vtworkerclient-exec-cmd.out")}, args...) - } - tmpProcess := exec.Command( - "vtworkerclient", - args..., - ) - log.Info(fmt.Sprintf("Executing vtworkerclient with arguments %v", strings.Join(tmpProcess.Args, " "))) - return tmpProcess.Run() -} - -func (vtworker *VtworkerProcess) ExecuteCommandInBg(args ...string) (*exec.Cmd, error) { - args = append([]string{"--vtworker_client_protocol", "grpc", - "--server", vtworker.Server, "--log_dir", vtworker.LogDir, "--stderrthreshold", "info"}, args...) - tmpProcess := exec.Command( - "vtworkerclient", - args..., - ) - log.Info(fmt.Sprintf("Executing vtworkerclient with arguments %v", strings.Join(tmpProcess.Args, " "))) - return tmpProcess, tmpProcess.Start() -} - -// ExecuteVtworkerCommand executes any vtworker command -func (vtworker *VtworkerProcess) ExecuteVtworkerCommand(port int, grpcPort int, args ...string) (err error) { - args = append([]string{ - "--port", fmt.Sprintf("%d", port), - "--executefetch_retry_time", vtworker.ExecuteRetryTime, - "--tablet_manager_protocol", "grpc", - "--tablet_protocol", "grpc", - "--topo_implementation", vtworker.CommonArg.TopoImplementation, - "--topo_global_server_address", vtworker.CommonArg.TopoGlobalAddress, - "--topo_global_root", vtworker.CommonArg.TopoGlobalRoot, - "--service_map", vtworker.ServiceMap, - "--grpc_port", fmt.Sprintf("%d", grpcPort), - "--cell", vtworker.Cell, - "--log_dir", vtworker.LogDir, "--stderrthreshold", "1"}, args...) - if *isCoverage { - args = append([]string{"--test.coverprofile=" + getCoveragePath("vtworker-exec-cmd.out")}, args...) - } - tmpProcess := exec.Command( - "vtworker", - args..., - ) - log.Info(fmt.Sprintf("Executing vtworker with arguments %v", strings.Join(tmpProcess.Args, " "))) - return tmpProcess.Run() -} - -// VtworkerProcessInstance returns a vtworker handle -// configured with the given Config. -// The process must be manually started by calling Setup() -func VtworkerProcessInstance(httpPort int, grpcPort int, topoPort int, hostname string, tmpDirectory string) *VtworkerProcess { - vtctl := VtctlProcessInstance(topoPort, hostname) - vtworker := &VtworkerProcess{ - Name: "vtworker", - Binary: "vtworker", - CommonArg: *vtctl, - ServiceMap: "grpc-tabletmanager,grpc-throttler,grpc-queryservice,grpc-updatestream,grpc-vtctl,grpc-vtworker,grpc-vtgateservice", - LogDir: tmpDirectory, - Port: httpPort, - GrpcPort: grpcPort, - ExecuteRetryTime: "1s", - CommandDisplayInterval: "10ms", - Directory: os.Getenv("VTDATAROOT"), - Server: fmt.Sprintf("%s:%d", hostname, grpcPort), - } - vtworker.VerifyURL = fmt.Sprintf("http://%s:%d/debug/vars", hostname, vtworker.Port) - return vtworker -} - -// GetVars returns map of vars -func (vtworker *VtworkerProcess) GetVars() (map[string]any, error) { - resultMap := make(map[string]any) - resp, err := http.Get(vtworker.VerifyURL) - if err != nil { - return nil, fmt.Errorf("error getting response from %s", vtworker.VerifyURL) - } - if resp.StatusCode == 200 { - respByte, _ := io.ReadAll(resp.Body) - err := json.Unmarshal(respByte, &resultMap) - if err != nil { - return nil, fmt.Errorf("not able to parse response body") - } - return resultMap, nil - } - return nil, fmt.Errorf("unsuccessful response") -} diff --git a/go/test/endtoend/keyspace/keyspace_test.go b/go/test/endtoend/keyspace/keyspace_test.go index 9c41a4fb97b..82477dacee7 100644 --- a/go/test/endtoend/keyspace/keyspace_test.go +++ b/go/test/endtoend/keyspace/keyspace_test.go @@ -120,9 +120,6 @@ func TestMain(m *testing.M) { if err := clusterForKSTest.StartKeyspace(*keyspaceUnsharded, []string{keyspaceUnshardedName}, 1, false); err != nil { return 1 } - if err := clusterForKSTest.VtctlclientProcess.ExecuteCommand("SetKeyspaceShardingInfo", "--", "--force", keyspaceUnshardedName, "keyspace_id", "uint64"); err != nil { - return 1 - } if err := clusterForKSTest.VtctlclientProcess.ExecuteCommand("RebuildKeyspaceGraph", keyspaceUnshardedName); err != nil { return 1 } diff --git a/go/test/endtoend/recovery/shardedrecovery/sharded_recovery_test.go b/go/test/endtoend/recovery/shardedrecovery/sharded_recovery_test.go deleted file mode 100644 index 3a150bf7ff1..00000000000 --- a/go/test/endtoend/recovery/shardedrecovery/sharded_recovery_test.go +++ /dev/null @@ -1,549 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package shardedrecovery - -import ( - "context" - "fmt" - "os/exec" - "strconv" - "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/recovery" - _ "vitess.io/vitess/go/vt/vtgate/grpcvtgateconn" - "vitess.io/vitess/go/vt/vtgate/vtgateconn" -) - -var ( - primary *cluster.Vttablet - replica1 *cluster.Vttablet - rdOnly *cluster.Vttablet - replica2 *cluster.Vttablet - replica3 *cluster.Vttablet - - shard0Primary *cluster.Vttablet - shard0Replica *cluster.Vttablet - shard0RdOnly *cluster.Vttablet - - shard1Primary *cluster.Vttablet - shard1Replica *cluster.Vttablet - shard1RdOnly *cluster.Vttablet - - localCluster *cluster.LocalProcessCluster - cell = cluster.DefaultCell - hostname = "localhost" - keyspaceName = "test_keyspace" - shardName = "0" - shard0Name = "-80" - shard1Name = "80-" - commonTabletArg = []string{ - "--vreplication_healthcheck_topology_refresh", "1s", - "--vreplication_healthcheck_retry_delay", "1s", - "--vreplication_retry_delay", "1s", - "--degraded_threshold", "5s", - "--lock_tables_timeout", "5s", - "--watch_replication_stream", - "--serving_state_grace_period", "1s"} - recoveryKS = "recovery_keyspace" - vtInsertTest = `create table vt_insert_test ( - id bigint auto_increment, - msg varchar(64), - primary key (id) - ) Engine=InnoDB` - vSchema = `{ - "sharded": true, - "vindexes": { - "hash": { - "type": "hash" - } - }, - "tables": { - "vt_insert_test": { - "column_vindexes": [ - { - "column": "id", - "name": "hash" - } - ] - } - } -}` -) - -// Test recovery from backup flow. - -// test_recovery will: -// - create a shard with primary and replica1 only -// - run InitShardPrimary -// - insert some data -// - take a backup -// - insert more data on the primary -// - perform a resharding -// - create a recovery keyspace -// - bring up tablet_replica2 in the new keyspace -// - check that new tablet does not have data created after backup -// - check that vtgate queries work correctly - -func TestUnShardedRecoveryAfterSharding(t *testing.T) { - defer cluster.PanicHandler(t) - _, err := initializeCluster(t) - defer localCluster.Teardown() - require.NoError(t, err) - err = localCluster.VtctlclientProcess.ApplySchema(keyspaceName, vtInsertTest) - require.NoError(t, err) - recovery.InsertData(t, primary, 1, keyspaceName) - cluster.VerifyRowsInTablet(t, replica1, keyspaceName, 1) - - // insert more data on the primary - recovery.InsertData(t, primary, 2, keyspaceName) - - // backup the replica - err = localCluster.VtctlclientProcess.ExecuteCommand("Backup", replica1.Alias) - require.NoError(t, err) - - // check that the backup shows up in the listing - output, err := localCluster.ListBackups("test_keyspace/0") - require.NoError(t, err) - assert.Equal(t, 1, len(output)) - assert.True(t, strings.HasSuffix(output[0], replica1.Alias)) - - // insert more data on the primary - recovery.InsertData(t, primary, 3, keyspaceName) - - err = localCluster.VtctlclientProcess.ApplyVSchema(keyspaceName, vSchema) - require.NoError(t, err) - - // create the split shards - for _, tablet := range []*cluster.Vttablet{shard0Primary, shard0Replica, shard0RdOnly, shard1Primary, shard1Replica, shard1RdOnly} { - tablet.VttabletProcess.ExtraArgs = []string{"--binlog_use_v3_resharding_mode=true"} - err = tablet.VttabletProcess.Setup() - require.NoError(t, err) - } - - err = localCluster.VtctlProcess.ExecuteCommand("InitShardPrimary", "--", "--force", "test_keyspace/-80", shard0Primary.Alias) - require.NoError(t, err) - - err = localCluster.VtctlProcess.ExecuteCommand("InitShardPrimary", "--", "--force", "test_keyspace/80-", shard1Primary.Alias) - require.NoError(t, err) - - shardedTablets := []*cluster.Vttablet{shard0Primary, shard0Replica, shard0RdOnly, shard1Primary, shard1Replica, shard1RdOnly} - - for _, tablet := range shardedTablets { - _ = tablet.VttabletProcess.WaitForTabletStatus("SERVING") - require.NoError(t, err) - } - - for _, tablet := range shardedTablets { - assert.Equal(t, tablet.VttabletProcess.GetTabletStatus(), "SERVING") - } - - // we need to create the schema, and the worker will do data copying - for _, keyspaceShard := range []string{"test_keyspace/-80", "test_keyspace/80-"} { - err = localCluster.VtctlclientProcess.ExecuteCommand("CopySchemaShard", "test_keyspace/0", keyspaceShard) - require.NoError(t, err) - } - - err = localCluster.VtctlclientProcess.ExecuteCommand("SplitClone", "test_keyspace", "0", "-80,80-") - require.NoError(t, err) - - err = localCluster.VtctlclientProcess.ExecuteCommand("MigrateServedTypes", "test_keyspace/0", "rdonly") - require.NoError(t, err) - - err = localCluster.VtctlclientProcess.ExecuteCommand("MigrateServedTypes", "test_keyspace/0", "replica") - require.NoError(t, err) - - // then serve primary from the split shards - err = localCluster.VtctlclientProcess.ExecuteCommand("MigrateServedTypes", "test_keyspace/0", "primary") - require.NoError(t, err) - - // remove the original tablets in the original shard - removeTablets(t, []*cluster.Vttablet{primary, replica1, rdOnly}) - - for _, tablet := range []*cluster.Vttablet{replica1, rdOnly} { - err = localCluster.VtctlclientProcess.ExecuteCommand("DeleteTablet", tablet.Alias) - require.NoError(t, err) - } - err = localCluster.VtctlclientProcess.ExecuteCommand("DeleteTablet", "--", "--allow_primary", primary.Alias) - require.NoError(t, err) - - // rebuild the serving graph, all mentions of the old shards should be gone - err = localCluster.VtctlclientProcess.ExecuteCommand("RebuildKeyspaceGraph", "test_keyspace") - require.NoError(t, err) - - // delete the original shard - err = localCluster.VtctlclientProcess.ExecuteCommand("DeleteShard", "test_keyspace/0") - require.NoError(t, err) - - // now bring up the recovery keyspace and a tablet, letting it restore from backup. - recovery.RestoreTablet(t, localCluster, replica2, recoveryKS, "0", keyspaceName, commonTabletArg) - - // check the new replica does not have the data - cluster.VerifyRowsInTablet(t, replica2, keyspaceName, 2) - - vtgateInstance := localCluster.NewVtgateInstance() - vtgateInstance.TabletTypesToWait = "REPLICA" - err = vtgateInstance.Setup() - localCluster.VtgateGrpcPort = vtgateInstance.GrpcPort - require.NoError(t, err) - err = vtgateInstance.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.primary", keyspaceName, shard0Name), 1) - require.NoError(t, err) - err = vtgateInstance.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.replica", keyspaceName, shard1Name), 1) - require.NoError(t, err) - err = vtgateInstance.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.primary", keyspaceName, shard0Name), 1) - require.NoError(t, err) - err = vtgateInstance.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.replica", keyspaceName, shard1Name), 1) - require.NoError(t, err) - err = vtgateInstance.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.replica", recoveryKS, shardName), 1) - require.NoError(t, err) - - // Build vtgate grpc connection - // check that vtgate doesn't route queries to new tablet - grpcAddress := fmt.Sprintf("%s:%d", localCluster.Hostname, localCluster.VtgateGrpcPort) - vtgateConn, err := vtgateconn.Dial(context.Background(), grpcAddress) - require.NoError(t, err) - - session := vtgateConn.Session("@replica", nil) - recovery.VerifyQueriesUsingVtgate(t, session, "select count(*) from vt_insert_test", "INT64(3)") - - // check that new tablet is accessible by using ks.table - recovery.VerifyQueriesUsingVtgate(t, session, "select count(*) from recovery_keyspace.vt_insert_test", "INT64(2)") - - // check that new tablet is accessible with 'use ks' - cluster.ExecuteQueriesUsingVtgate(t, session, "use `recovery_keyspace@replica`") - recovery.VerifyQueriesUsingVtgate(t, session, "select count(*) from vt_insert_test", "INT64(2)") - - // check that new tablet is accessible with `use ks:shard` - cluster.ExecuteQueriesUsingVtgate(t, session, "use `recovery_keyspace:0@replica`") - recovery.VerifyQueriesUsingVtgate(t, session, "select count(*) from vt_insert_test", "INT64(2)") - - vtgateConn.Close() - err = vtgateInstance.TearDown() - require.NoError(t, err) -} - -// Test recovery from backup flow. - -// test_recovery will: -// - create a shard with primary and replica1 only -// - run InitShardPrimary -// - insert some data -// - perform a resharding -// - take a backup of both new shards -// - insert more data on the primaries of both shards -// - create a recovery keyspace -// - bring up tablet_replica2 and tablet_replica3 in the new keyspace -// - check that new tablets do not have data created after backup -// - check that vtgate queries work correctly - -func TestShardedRecovery(t *testing.T) { - - defer cluster.PanicHandler(t) - _, err := initializeCluster(t) - defer localCluster.Teardown() - require.NoError(t, err) - err = localCluster.VtctlclientProcess.ApplySchema(keyspaceName, vtInsertTest) - require.NoError(t, err) - recovery.InsertData(t, primary, 1, keyspaceName) - - cluster.VerifyRowsInTablet(t, replica1, keyspaceName, 1) - - // insert more data on the primary - recovery.InsertData(t, primary, 4, keyspaceName) - - err = localCluster.VtctlclientProcess.ApplyVSchema(keyspaceName, vSchema) - require.NoError(t, err) - - // create the split shards - shardedTablets := []*cluster.Vttablet{shard0Primary, shard0Replica, shard0RdOnly, shard1Primary, shard1Replica, shard1RdOnly} - for _, tablet := range shardedTablets { - tablet.VttabletProcess.ExtraArgs = []string{"--binlog_use_v3_resharding_mode=true"} - err = tablet.VttabletProcess.Setup() - require.NoError(t, err) - } - - err = localCluster.VtctlProcess.ExecuteCommand("InitShardPrimary", "--", "--force", "test_keyspace/-80", shard0Primary.Alias) - require.NoError(t, err) - - err = localCluster.VtctlProcess.ExecuteCommand("InitShardPrimary", "--", "--force", "test_keyspace/80-", shard1Primary.Alias) - require.NoError(t, err) - - for _, tablet := range shardedTablets { - _ = tablet.VttabletProcess.WaitForTabletStatus("SERVING") - require.NoError(t, err) - } - - // we need to create the schema, and the worker will do data copying - for _, keyspaceShard := range []string{"test_keyspace/-80", "test_keyspace/80-"} { - err = localCluster.VtctlclientProcess.ExecuteCommand("CopySchemaShard", "test_keyspace/0", keyspaceShard) - require.NoError(t, err) - } - - err = localCluster.VtctlclientProcess.ExecuteCommand("SplitClone", "test_keyspace", "0", "-80,80-") - require.NoError(t, err) - - err = localCluster.VtctlclientProcess.ExecuteCommand("MigrateServedTypes", "test_keyspace/0", "rdonly") - require.NoError(t, err) - - err = localCluster.VtctlclientProcess.ExecuteCommand("MigrateServedTypes", "test_keyspace/0", "replica") - require.NoError(t, err) - - // then serve primary from the split shards - err = localCluster.VtctlclientProcess.ExecuteCommand("MigrateServedTypes", "test_keyspace/0", "primary") - require.NoError(t, err) - - // remove the original tablets in the original shard - removeTablets(t, []*cluster.Vttablet{primary, replica1, rdOnly}) - - for _, tablet := range []*cluster.Vttablet{replica1, rdOnly} { - err = localCluster.VtctlclientProcess.ExecuteCommand("DeleteTablet", tablet.Alias) - require.NoError(t, err) - } - err = localCluster.VtctlclientProcess.ExecuteCommand("DeleteTablet", "--", "--allow_primary", primary.Alias) - require.NoError(t, err) - - // rebuild the serving graph, all mentions of the old shards should be gone - err = localCluster.VtctlclientProcess.ExecuteCommand("RebuildKeyspaceGraph", "test_keyspace") - require.NoError(t, err) - - // delete the original shard - err = localCluster.VtctlclientProcess.ExecuteCommand("DeleteShard", "test_keyspace/0") - require.NoError(t, err) - - qr, err := shard0Primary.VttabletProcess.QueryTablet("select count(*) from vt_insert_test", keyspaceName, true) - require.NoError(t, err) - shard0CountStr := qr.Rows[0][0].ToString() - shard0Count, err := strconv.Atoi(shard0CountStr) - require.NoError(t, err) - var shard0TestID string - if shard0Count > 0 { - qr, err := shard0Primary.VttabletProcess.QueryTablet("select id from vt_insert_test", keyspaceName, true) - require.NoError(t, err) - shard0TestID = qr.Rows[0][0].ToString() - require.NoError(t, err) - } - - qr, err = shard1Primary.VttabletProcess.QueryTablet("select count(*) from vt_insert_test", keyspaceName, true) - require.NoError(t, err) - shard1CountStr := qr.Rows[0][0].ToString() - shard1Count, err := strconv.Atoi(shard1CountStr) - require.NoError(t, err) - var shard1TestID string - if shard1Count > 0 { - qr, err := shard1Primary.VttabletProcess.QueryTablet("select id from vt_insert_test", keyspaceName, true) - require.NoError(t, err) - shard1TestID = qr.Rows[0][0].ToString() - require.NoError(t, err) - } - - // backup the new shards - err = localCluster.VtctlclientProcess.ExecuteCommand("Backup", shard0Replica.Alias) - require.NoError(t, err) - err = localCluster.VtctlclientProcess.ExecuteCommand("Backup", shard1Replica.Alias) - require.NoError(t, err) - - // check that the backup shows up in the listing - output, err := localCluster.ListBackups("test_keyspace/-80") - require.NoError(t, err) - assert.Equal(t, 1, len(output)) - assert.True(t, strings.HasSuffix(output[0], shard0Replica.Alias)) - - output, err = localCluster.ListBackups("test_keyspace/80-") - require.NoError(t, err) - assert.Equal(t, 1, len(output)) - assert.True(t, strings.HasSuffix(output[0], shard1Replica.Alias)) - - vtgateInstance := localCluster.NewVtgateInstance() - vtgateInstance.TabletTypesToWait = "PRIMARY" - err = vtgateInstance.Setup() - localCluster.VtgateGrpcPort = vtgateInstance.GrpcPort - require.NoError(t, err) - err = vtgateInstance.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.primary", keyspaceName, shard0Name), 1) - require.NoError(t, err) - err = vtgateInstance.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.primary", keyspaceName, shard1Name), 1) - require.NoError(t, err) - - // Build vtgate grpc connection - // check that vtgate doesn't route queries to new tablet - grpcAddress := fmt.Sprintf("%s:%d", localCluster.Hostname, localCluster.VtgateGrpcPort) - vtgateConn, err := vtgateconn.Dial(context.Background(), grpcAddress) - require.NoError(t, err) - session := vtgateConn.Session("@primary", nil) - cluster.ExecuteQueriesUsingVtgate(t, session, "insert into vt_insert_test (id, msg) values (2,'test 2')") - cluster.ExecuteQueriesUsingVtgate(t, session, "insert into vt_insert_test (id, msg) values (3,'test 3')") - - vtgateConn.Close() - err = vtgateInstance.TearDown() - require.NoError(t, err) - - // now bring up the recovery keyspace and 2 tablets, letting it restore from backup. - recovery.RestoreTablet(t, localCluster, replica2, recoveryKS, "-80", keyspaceName, commonTabletArg) - recovery.RestoreTablet(t, localCluster, replica3, recoveryKS, "80-", keyspaceName, commonTabletArg) - - // check the new replicas have the correct number of rows - cluster.VerifyRowsInTablet(t, replica2, keyspaceName, shard0Count) - cluster.VerifyRowsInTablet(t, replica3, keyspaceName, shard1Count) - - // start vtgate - vtgateInstance = localCluster.NewVtgateInstance() - vtgateInstance.TabletTypesToWait = "REPLICA" - err = vtgateInstance.Setup() - localCluster.VtgateGrpcPort = vtgateInstance.GrpcPort - require.NoError(t, err) - err = vtgateInstance.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.primary", keyspaceName, shard0Name), 1) - require.NoError(t, err) - err = vtgateInstance.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.replica", keyspaceName, shard1Name), 1) - require.NoError(t, err) - err = vtgateInstance.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.primary", keyspaceName, shard0Name), 1) - require.NoError(t, err) - err = vtgateInstance.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.replica", keyspaceName, shard1Name), 1) - require.NoError(t, err) - err = vtgateInstance.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.replica", recoveryKS, shard0Name), 1) - require.NoError(t, err) - err = vtgateInstance.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.replica", recoveryKS, shard1Name), 1) - require.NoError(t, err) - - // check that vtgate doesn't route queries to new tablet - grpcAddress = fmt.Sprintf("%s:%d", localCluster.Hostname, localCluster.VtgateGrpcPort) - vtgateConn, err = vtgateconn.Dial(context.Background(), grpcAddress) - require.NoError(t, err) - session = vtgateConn.Session("@replica", nil) - recovery.VerifyQueriesUsingVtgate(t, session, "select count(*) from vt_insert_test", "INT64(4)") - - // check that new keyspace is accessible by using ks.table - recovery.VerifyQueriesUsingVtgate(t, session, "select count(*) from recovery_keyspace.vt_insert_test", "INT64(2)") - - // check that new keyspace is accessible with 'use ks' - cluster.ExecuteQueriesUsingVtgate(t, session, "use recovery_keyspace@replica") - recovery.VerifyQueriesUsingVtgate(t, session, "select count(*) from vt_insert_test", "INT64(2)") - - // check that new tablet is accessible with use `ks:shard` - cluster.ExecuteQueriesUsingVtgate(t, session, "use `recovery_keyspace:-80@replica`") - recovery.VerifyQueriesUsingVtgate(t, session, "select count(*) from vt_insert_test", "INT64("+shard0CountStr+")") - recovery.VerifyQueriesUsingVtgate(t, session, "select id from vt_insert_test", "INT64("+shard0TestID+")") - - cluster.ExecuteQueriesUsingVtgate(t, session, "use `recovery_keyspace:80-@replica`") - recovery.VerifyQueriesUsingVtgate(t, session, "select count(*) from vt_insert_test", "INT64("+shard1CountStr+")") - recovery.VerifyQueriesUsingVtgate(t, session, "select id from vt_insert_test", "INT64("+shard1TestID+")") - - vtgateConn.Close() - err = vtgateInstance.TearDown() - require.NoError(t, err) -} - -func removeTablets(t *testing.T, tablets []*cluster.Vttablet) { - var mysqlProcs []*exec.Cmd - for _, tablet := range tablets { - proc, _ := tablet.MysqlctlProcess.StopProcess() - mysqlProcs = append(mysqlProcs, proc) - tablet.VttabletProcess.TearDown() - } - for _, proc := range mysqlProcs { - err := proc.Wait() - require.NoError(t, err) - } -} - -func initializeCluster(t *testing.T) (int, error) { - - localCluster = cluster.NewCluster(cell, hostname) - - // Start topo server - err := localCluster.StartTopo() - if err != nil { - return 1, err - } - // Start keyspace - keyspace := &cluster.Keyspace{ - Name: keyspaceName, - } - localCluster.Keyspaces = append(localCluster.Keyspaces, *keyspace) - - // TODO : handle shards properly - shard := &cluster.Shard{ - Name: shardName, - } - shard0 := &cluster.Shard{ - Name: shard0Name, - } - shard1 := &cluster.Shard{ - Name: shard1Name, - } - - // Defining all the tablets - primary = localCluster.NewVttabletInstance("replica", 0, "") - replica1 = localCluster.NewVttabletInstance("replica", 0, "") - rdOnly = localCluster.NewVttabletInstance("rdonly", 0, "") - replica2 = localCluster.NewVttabletInstance("replica", 0, "") - replica3 = localCluster.NewVttabletInstance("replica", 0, "") - shard0Primary = localCluster.NewVttabletInstance("replica", 0, "") - shard0Replica = localCluster.NewVttabletInstance("replica", 0, "") - shard0RdOnly = localCluster.NewVttabletInstance("rdonly", 0, "") - shard1Primary = localCluster.NewVttabletInstance("replica", 0, "") - shard1Replica = localCluster.NewVttabletInstance("replica", 0, "") - shard1RdOnly = localCluster.NewVttabletInstance("rdonly", 0, "") - - shard.Vttablets = []*cluster.Vttablet{primary, replica1, rdOnly, replica2, replica3} - shard0.Vttablets = []*cluster.Vttablet{shard0Primary, shard0Replica, shard0RdOnly} - shard1.Vttablets = []*cluster.Vttablet{shard1Primary, shard1Replica, shard1RdOnly} - - localCluster.VtTabletExtraArgs = append(localCluster.VtTabletExtraArgs, commonTabletArg...) - localCluster.VtTabletExtraArgs = append(localCluster.VtTabletExtraArgs, "--restore_from_backup", "--enable_semi_sync") - - err = localCluster.SetupCluster(keyspace, []cluster.Shard{*shard, *shard0, *shard1}) - require.NoError(t, err) - vtctldClientProcess := cluster.VtctldClientProcessInstance("localhost", localCluster.VtctldProcess.GrpcPort, localCluster.TmpDirectory) - out, err := vtctldClientProcess.ExecuteCommandWithOutput("SetKeyspaceDurabilityPolicy", keyspaceName, "--durability-policy=semi_sync") - require.NoError(t, err, out) - // Start MySql - var mysqlCtlProcessList []*exec.Cmd - for _, shard := range localCluster.Keyspaces[0].Shards { - for _, tablet := range shard.Vttablets { - if proc, err := tablet.MysqlctlProcess.StartProcess(); err != nil { - t.Fatal(err) - } else { - mysqlCtlProcessList = append(mysqlCtlProcessList, proc) - } - } - } - - // Wait for mysql processes to start - for _, proc := range mysqlCtlProcessList { - if err := proc.Wait(); err != nil { - t.Fatal(err) - } - } - - for _, tablet := range []cluster.Vttablet{*primary, *replica1, *rdOnly} { - if err = tablet.VttabletProcess.CreateDB(keyspaceName); err != nil { - return 1, err - } - if err = tablet.VttabletProcess.Setup(); err != nil { - return 1, err - } - } - - if err = localCluster.VtctlclientProcess.InitShardPrimary(keyspaceName, shard.Name, cell, primary.TabletUID); err != nil { - return 1, err - } - - return 0, nil -} diff --git a/go/test/endtoend/recovery/unshardedrecovery/recovery.go b/go/test/endtoend/recovery/unshardedrecovery/recovery.go index 0f85ad5131b..db76cc3654f 100644 --- a/go/test/endtoend/recovery/unshardedrecovery/recovery.go +++ b/go/test/endtoend/recovery/unshardedrecovery/recovery.go @@ -30,7 +30,6 @@ import ( "vitess.io/vitess/go/test/endtoend/cluster" "vitess.io/vitess/go/test/endtoend/recovery" - "vitess.io/vitess/go/test/endtoend/sharding/initialsharding" "vitess.io/vitess/go/vt/log" "vitess.io/vitess/go/vt/vtgate/vtgateconn" ) @@ -92,11 +91,11 @@ func TestMainImpl(m *testing.M) { } localCluster.Keyspaces = append(localCluster.Keyspaces, *keyspace) - dbCredentialFile = initialsharding.WriteDbCredentialToTmp(localCluster.TmpDirectory) + dbCredentialFile = cluster.WriteDbCredentialToTmp(localCluster.TmpDirectory) initDb, _ := os.ReadFile(path.Join(os.Getenv("VTROOT"), "/config/init_db.sql")) sql := string(initDb) newInitDBFile = path.Join(localCluster.TmpDirectory, "init_db_with_passwords.sql") - sql = sql + initialsharding.GetPasswordUpdateSQL(localCluster) + sql = sql + cluster.GetPasswordUpdateSQL(localCluster) // https://github.com/vitessio/vitess/issues/8315 oldAlterTableMode := ` SET GLOBAL old_alter_table = ON; diff --git a/go/test/endtoend/sharding/base_sharding.go b/go/test/endtoend/sharding/base_sharding.go deleted file mode 100644 index b88d2644311..00000000000 --- a/go/test/endtoend/sharding/base_sharding.go +++ /dev/null @@ -1,491 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - -*/ - -package sharding - -import ( - "context" - "fmt" - "math" - "net/http" - "path" - "reflect" - "strconv" - "strings" - "testing" - "time" - - "vitess.io/vitess/go/sqltypes" - - "vitess.io/vitess/go/mysql" - - "vitess.io/vitess/go/vt/log" - - "vitess.io/vitess/go/json2" - "vitess.io/vitess/go/test/endtoend/cluster" - querypb "vitess.io/vitess/go/vt/proto/query" - "vitess.io/vitess/go/vt/proto/topodata" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -var ( - lotRange1 uint64 = 0xA000000000000000 - lotRange2 uint64 = 0xE000000000000000 - // InsertTabletTemplateKsID common insert format to be used for different tests - InsertTabletTemplateKsID = `insert into %s (id, msg) values (%d, '%s') /* id:%d */` -) - -const ( - maxRowsToFetch = 10000 -) - -// CheckSrvKeyspace verifies the schema with expectedPartition -func CheckSrvKeyspace(t *testing.T, cell string, ksname string, expectedPartition map[topodata.TabletType][]string, ci cluster.LocalProcessCluster) { - srvKeyspace := GetSrvKeyspace(t, cell, ksname, ci) - - currentPartition := map[topodata.TabletType][]string{} - - for _, partition := range srvKeyspace.Partitions { - currentPartition[partition.ServedType] = []string{} - for _, shardRef := range partition.ShardReferences { - currentPartition[partition.ServedType] = append(currentPartition[partition.ServedType], shardRef.Name) - } - } - - assert.True(t, reflect.DeepEqual(currentPartition, expectedPartition)) -} - -// GetSrvKeyspace return the Srv Keyspace structure -func GetSrvKeyspace(t *testing.T, cell string, ksname string, ci cluster.LocalProcessCluster) *topodata.SrvKeyspace { - output, err := ci.VtctlclientProcess.ExecuteCommandWithOutput("GetSrvKeyspace", cell, ksname) - require.Nil(t, err) - var srvKeyspace topodata.SrvKeyspace - - err = json2.Unmarshal([]byte(output), &srvKeyspace) - require.Nil(t, err) - return &srvKeyspace -} - -// VerifyTabletHealth checks that the tablet URL is reachable. -func VerifyTabletHealth(t *testing.T, vttablet cluster.Vttablet, hostname string) { - tabletURL := fmt.Sprintf("http://%s:%d/healthz", hostname, vttablet.HTTPPort) - resp, err := http.Get(tabletURL) - require.Nil(t, err) - assert.Equal(t, resp.StatusCode, 200) -} - -// CheckValues check value from sql query to table with expected values -func CheckValues(t *testing.T, vttablet cluster.Vttablet, id uint64, msg string, exists bool, tableName string, ks string, keyType querypb.Type, dbConn *mysql.Conn) bool { - query := fmt.Sprintf("select id, msg from %s where id = %d", tableName, id) - if keyType == querypb.Type_VARBINARY { - query = fmt.Sprintf("select id, msg from %s where id = '%d'", tableName, id) - } - var result *sqltypes.Result - if dbConn != nil { - r1, err := dbConn.ExecuteFetch(query, maxRowsToFetch, true) - require.Nil(t, err) - result = r1 - } else { - r2, err := vttablet.VttabletProcess.QueryTablet(query, ks, true) - require.Nil(t, err) - result = r2 - } - - isFound := false - if exists && len(result.Rows) > 0 { - if keyType == querypb.Type_VARBINARY { - isFound = assert.Equal(t, fmt.Sprintf("%v", result.Rows), fmt.Sprintf(`[[VARBINARY("%d") VARCHAR("%s")]]`, id, msg)) - } else { - isFound = assert.Equal(t, fmt.Sprintf("%v", result.Rows), fmt.Sprintf(`[[UINT64(%d) VARCHAR("%s")]]`, id, msg)) - } - - } else { - assert.Equal(t, len(result.Rows), 0) - } - return isFound -} - -// CheckDestinationPrimary performs multiple checks on a destination primary. -func CheckDestinationPrimary(t *testing.T, vttablet cluster.Vttablet, sourceShards []string, ci cluster.LocalProcessCluster) { - _ = vttablet.VttabletProcess.WaitForBinLogPlayerCount(len(sourceShards)) - CheckBinlogPlayerVars(t, vttablet, sourceShards, 0) - checkStreamHealthEqualsBinlogPlayerVars(t, vttablet, len(sourceShards), ci) -} - -// CheckBinlogPlayerVars Checks the binlog player variables are correctly exported. -func CheckBinlogPlayerVars(t *testing.T, vttablet cluster.Vttablet, sourceShards []string, replicationLagSeconds int64) { - tabletVars := vttablet.VttabletProcess.GetVars() - - assert.Contains(t, tabletVars, "VReplicationStreamCount") - assert.Contains(t, tabletVars, "VReplicationLagSecondsMax") - assert.Contains(t, tabletVars, "VReplicationLagSeconds") - assert.Contains(t, tabletVars, "VReplicationSource") - assert.Contains(t, tabletVars, "VReplicationSourceTablet") - - streamCountStr := fmt.Sprintf("%v", reflect.ValueOf(tabletVars["VReplicationStreamCount"])) - streamCount, _ := strconv.Atoi(streamCountStr) - assert.Equal(t, streamCount, len(sourceShards)) - - replicationSourceObj := reflect.ValueOf(tabletVars["VReplicationSource"]) - replicationSourceValue := []string{} - - assert.Equal(t, len(replicationSourceObj.MapKeys()), len(reflect.ValueOf(tabletVars["VReplicationSourceTablet"]).MapKeys())) - - for _, key := range replicationSourceObj.MapKeys() { - replicationSourceValue = append(replicationSourceValue, - fmt.Sprintf("%v", replicationSourceObj.MapIndex(key))) - } - - for _, shard := range sourceShards { - assert.Containsf(t, replicationSourceValue, shard, "Source shard is not matched with vReplication shard value") - } - - if replicationLagSeconds != 0 { - vreplicationLagMaxStr := fmt.Sprintf("%v", reflect.ValueOf(tabletVars["VReplicationLagSecondsMax"])) - vreplicationLagMax, _ := strconv.ParseFloat(vreplicationLagMaxStr, 64) - - assert.True(t, vreplicationLagMax < float64(replicationLagSeconds)) - - replicationLagObj := reflect.ValueOf(tabletVars["VReplicationLagSeconds"]) - for _, key := range replicationSourceObj.MapKeys() { - str := fmt.Sprintf("%v", replicationLagObj.MapIndex(key)) - flt, _ := strconv.ParseFloat(str, 64) - assert.True(t, flt < float64(replicationLagSeconds)) - } - } -} - -// checkStreamHealthEqualsBinlogPlayerVars - Checks the variables exported by streaming health check match vars. -func checkStreamHealthEqualsBinlogPlayerVars(t *testing.T, vttablet cluster.Vttablet, count int, ci cluster.LocalProcessCluster) { - tabletVars := vttablet.VttabletProcess.GetVars() - - streamCountStr := fmt.Sprintf("%v", reflect.ValueOf(tabletVars["VReplicationStreamCount"])) - streamCount, _ := strconv.Atoi(streamCountStr) - vreplicationLagMaxStr := fmt.Sprintf("%v", reflect.ValueOf(tabletVars["VReplicationLagSecondsMax"])) - vreplicationLagMax, _ := strconv.ParseFloat(vreplicationLagMaxStr, 64) - - assert.Equal(t, streamCount, count) - // Enforce health check because it's not running by default as - // tablets may not be started with it, or may not run it in time. - _ = ci.VtctlclientProcess.ExecuteCommand("RunHealthCheck", vttablet.Alias) - streamHealth, err := ci.VtctlclientProcess.ExecuteCommandWithOutput("VtTabletStreamHealth", "--", "--count", "1", vttablet.Alias) - require.Nil(t, err) - - var streamHealthResponse querypb.StreamHealthResponse - err = json2.Unmarshal([]byte(streamHealth), &streamHealthResponse) - require.Nil(t, err, "error should be Nil") - assert.Equal(t, streamHealthResponse.Serving, false) - assert.NotNil(t, streamHealthResponse.RealtimeStats) - assert.Equal(t, streamHealthResponse.RealtimeStats.HealthError, "") - assert.NotNil(t, streamHealthResponse.RealtimeStats.BinlogPlayersCount) - - assert.Equal(t, streamCount, int(streamHealthResponse.RealtimeStats.BinlogPlayersCount)) - assert.Equal(t, vreplicationLagMax, float64(streamHealthResponse.RealtimeStats.FilteredReplicationLagSeconds)) -} - -// CheckBinlogServerVars checks the binlog server variables are correctly exported. -func CheckBinlogServerVars(t *testing.T, vttablet cluster.Vttablet, minStatement int, minTxn int, isVerticalSplit bool) { - resultMap := vttablet.VttabletProcess.GetVars() - skey := "UpdateStreamKeyRangeStatements" - tkey := "UpdateStreamKeyRangeTransactions" - if isVerticalSplit { - skey = "UpdateStreamTablesStatements" - tkey = "UpdateStreamTablesTransactions" - } - assert.Contains(t, resultMap, skey) - assert.Contains(t, resultMap, tkey) - if minStatement > 0 { - value := fmt.Sprintf("%v", reflect.ValueOf(resultMap[skey])) - iValue, _ := strconv.Atoi(value) - assert.True(t, iValue >= minStatement, fmt.Sprintf("only got %d < %d statements", iValue, minStatement)) - } - if minTxn > 0 { - value := fmt.Sprintf("%v", reflect.ValueOf(resultMap[tkey])) - iValue, _ := strconv.Atoi(value) - assert.True(t, iValue >= minTxn, fmt.Sprintf("only got %d < %d transactions", iValue, minTxn)) - } -} - -// InsertLots inserts multiple values to vttablet -func InsertLots(t *testing.T, count uint64, vttablet cluster.Vttablet, table string, ks string) { - var query1, query2 string - var i uint64 - dbConn := getDBConnFromTablet(t, &vttablet, ks) - defer dbConn.Close() - for i = 0; i < count; i++ { - query1 = fmt.Sprintf(InsertTabletTemplateKsID, table, lotRange1+i, fmt.Sprintf("msg-range1-%d", 10000+i), lotRange1+i) - query2 = fmt.Sprintf(InsertTabletTemplateKsID, table, lotRange2+i, fmt.Sprintf("msg-range2-%d", 20000+i), lotRange2+i) - - // insert first query - executeQueryInTransaction(t, query1, dbConn) - executeQueryInTransaction(t, query2, dbConn) - } -} - -func executeQueryInTransaction(t *testing.T, query string, dbConn *mysql.Conn) { - dbConn.ExecuteFetch("begin", maxRowsToFetch, true) - _, err := dbConn.ExecuteFetch(query, maxRowsToFetch, true) - require.NoError(t, err) - dbConn.ExecuteFetch("commit", maxRowsToFetch, true) -} - -// ExecuteOnTablet executes a write query on specified vttablet -// It should always be called with a primary tablet for the keyspace/shard -func ExecuteOnTablet(t *testing.T, query string, vttablet cluster.Vttablet, ks string, expectFail bool) { - _, _ = vttablet.VttabletProcess.QueryTablet("begin", ks, true) - _, err := vttablet.VttabletProcess.QueryTablet(query, ks, true) - if expectFail { - require.Error(t, err) - } else { - require.Nil(t, err) - } - _, _ = vttablet.VttabletProcess.QueryTablet("commit", ks, true) -} - -// InsertMultiValues inserts a multiple values to vttablet -func InsertMultiValues(t *testing.T, tablet cluster.Vttablet, keyspaceName string, tableName string, - fixedParentID int, ids []int, msgs []string, ksIDs []uint64) { - queryStr := fmt.Sprintf("insert into %s (parent_id, id, msg, custom_ksid_col) values", tableName) - valueSQL := "" - keyspaceIds := "" - valueIds := "" - for i := range ids { - valueSQL += fmt.Sprintf(`(%d, %d, "%s", %d)`, fixedParentID, ids[i], msgs[i], ksIDs[i]) - keyspaceIds += fmt.Sprintf("%d", ksIDs[i]) - valueIds += fmt.Sprintf("%d", ids[i]) - if i < len(ids)-1 { - valueSQL += "," - keyspaceIds += "," - valueIds += "," - } - } - - queryStr += valueSQL - queryStr += fmt.Sprintf(" /* vtgate:: keyspace_id:%s */", keyspaceIds) - queryStr += fmt.Sprintf(" /* id:%s */", valueIds) - ExecuteOnTablet(t, queryStr, tablet, keyspaceName, false) -} - -// CheckLotsTimeout waits till all values are inserted -func CheckLotsTimeout(t *testing.T, vttablet cluster.Vttablet, count uint64, table string, ks string, keyType querypb.Type, pctFound int) bool { - timeout := time.Now().Add(10 * time.Second) - var percentFound float64 - for time.Now().Before(timeout) { - percentFound = checkLots(t, vttablet, count, table, ks, keyType) - if int(math.Round(percentFound)) == pctFound { - return true - } - time.Sleep(300 * time.Millisecond) - } - log.Infof("expected pct %d, got pct %f", pctFound, percentFound) - return false -} - -func checkLots(t *testing.T, vttablet cluster.Vttablet, count uint64, table string, ks string, keyType querypb.Type) float64 { - var isFound bool - var totalFound int - var i uint64 - dbConn := getDBConnFromTablet(t, &vttablet, ks) - defer dbConn.Close() - - for i = 0; i < count; i++ { - isFound = CheckValues(t, vttablet, - lotRange1+i, fmt.Sprintf("msg-range1-%d", 10000+i), true, table, ks, keyType, dbConn) - if isFound { - totalFound++ - } - - isFound = CheckValues(t, vttablet, - lotRange2+i, fmt.Sprintf("msg-range2-%d", 20000+i), true, table, ks, keyType, dbConn) - if isFound { - totalFound++ - } - } - log.Infof("Total found %d", totalFound) - return float64(float64(totalFound) * 100 / float64(count) / 2) -} - -// CheckTabletQueryServices check that the query service is enabled or disabled on the specified tablets. -func CheckTabletQueryServices(t *testing.T, vttablets []cluster.Vttablet, expectedStatus string, tabletControlEnabled bool, ci cluster.LocalProcessCluster) { - for _, tablet := range vttablets { - CheckTabletQueryService(t, tablet, expectedStatus, tabletControlEnabled, ci) - } -} - -// CheckTabletQueryService check that the query service is enabled or disabled on the tablet -func CheckTabletQueryService(t *testing.T, vttablet cluster.Vttablet, expectedStatus string, tabletControlEnabled bool, ci cluster.LocalProcessCluster) { - tabletStatus := vttablet.VttabletProcess.GetTabletStatus() - assert.Equal(t, tabletStatus, expectedStatus) - - queryServiceDisabled := "TabletControl.DisableQueryService set" - status := vttablet.VttabletProcess.GetStatus() - if tabletControlEnabled { - assert.Contains(t, status, queryServiceDisabled) - } else { - assert.NotContains(t, status, queryServiceDisabled) - } - - if vttablet.Type == "rdonly" { - // Run RunHealthCheck to be sure the tablet doesn't change its serving state. - _ = ci.VtctlclientProcess.ExecuteCommand("RunHealthCheck", vttablet.Alias) - tabletStatus = vttablet.VttabletProcess.GetTabletStatus() - assert.Equal(t, tabletStatus, expectedStatus) - } -} - -// CheckShardQueryServices checks DisableQueryService for all shards -func CheckShardQueryServices(t *testing.T, ci cluster.LocalProcessCluster, shards []cluster.Shard, cell string, - keyspaceName string, tabletType topodata.TabletType, expectedState bool) { - for _, shard := range shards { - CheckShardQueryService(t, ci, cell, keyspaceName, shard.Name, tabletType, expectedState) - } -} - -// CheckShardQueryService checks DisableQueryService in the shard record's TabletControlMap. -func CheckShardQueryService(t *testing.T, ci cluster.LocalProcessCluster, cell string, keyspaceName string, - shardName string, tabletType topodata.TabletType, expectedState bool) { - // We assume that query service should be enabled unless - // DisableQueryService is explicitly True - queryServiceEnabled := true - srvKeyspace := GetSrvKeyspace(t, cell, keyspaceName, ci) - for _, partition := range srvKeyspace.Partitions { - tType := partition.GetServedType() - if tabletType != tType { - continue - } - for _, shardTabletControl := range partition.GetShardTabletControls() { - if shardTabletControl.GetName() == shardName { - if shardTabletControl.GetQueryServiceDisabled() { - queryServiceEnabled = false - } - } - } - } - - assert.True(t, queryServiceEnabled == expectedState, - fmt.Sprintf("shard %s does not have the correct query service state: got %t but expected %t", - shardName, queryServiceEnabled, expectedState)) - -} - -// GetShardInfo return the Shard information -func GetShardInfo(t *testing.T, shard1Ks string, ci cluster.LocalProcessCluster) *topodata.Shard { - output, err := ci.VtctlclientProcess.ExecuteCommandWithOutput("GetShard", shard1Ks) - require.Nil(t, err) - var shard topodata.Shard - err = json2.Unmarshal([]byte(output), &shard) - require.Nil(t, err) - return &shard -} - -// checkThrottlerServiceMaxRates Checks the vtctl ThrottlerMaxRates and ThrottlerSetRate commands. -func checkThrottlerServiceMaxRates(t *testing.T, server string, names []string, rate int, ci cluster.LocalProcessCluster) { - // Avoid flakes by waiting for all throttlers. (Necessary because filtered - // replication on vttablet will register the throttler asynchronously.) - var output string - var err error - startTime := time.Now() - msg := fmt.Sprintf("%d active throttler(s)", len(names)) - for { - output, err = ci.VtctlclientProcess.ExecuteCommandWithOutput("ThrottlerMaxRates", "--", "--server", server) - require.Nil(t, err) - if strings.Contains(output, msg) || (time.Now().After(startTime.Add(2 * time.Minute))) { - break - } - time.Sleep(2 * time.Second) - } - assert.Contains(t, output, msg) - - for _, name := range names { - str := fmt.Sprintf("| %s | %d |", name, rate) - assert.Contains(t, output, str) - } - - // Check that it's possible to change the max rate on the throttler. - newRate := "unlimited" - output, err = ci.VtctlclientProcess.ExecuteCommandWithOutput("ThrottlerSetMaxRate", "--", "--server", server, newRate) - require.Nil(t, err) - assert.Contains(t, output, msg) - - output, err = ci.VtctlclientProcess.ExecuteCommandWithOutput("ThrottlerMaxRates", "--", "--server", server) - require.Nil(t, err) - for _, name := range names { - str := fmt.Sprintf("| %s | %s |", name, newRate) - assert.Contains(t, output, str) - } - assert.Contains(t, output, msg) -} - -// checkThrottlerServiceConfiguration checks the vtctl (Get|Update|Reset)ThrottlerConfiguration commands. -func checkThrottlerServiceConfiguration(t *testing.T, server string, names []string, ci cluster.LocalProcessCluster) { - output, err := ci.VtctlclientProcess.ExecuteCommandWithOutput( - "UpdateThrottlerConfiguration", "--", "--server", server, - "--copy_zero_values", - "target_replication_lag_sec:12345 "+ - "max_replication_lag_sec:65789 "+ - "initial_rate:3 max_increase:0.4 "+ - "emergency_decrease:0.5 "+ - "min_duration_between_increases_sec:6 "+ - "max_duration_between_increases_sec:7 "+ - "min_duration_between_decreases_sec:8 "+ - "spread_backlog_across_sec:9 "+ - "ignore_n_slowest_replicas:0 "+ - "ignore_n_slowest_rdonlys:0 "+ - "age_bad_rate_after_sec:12 "+ - "bad_rate_increase:0.13 "+ - "max_rate_approach_threshold: 0.9 ", - ) - require.Nil(t, err) - msg := fmt.Sprintf("%d active throttler(s)", len(names)) - assert.Contains(t, output, msg) - - output, err = ci.VtctlclientProcess.ExecuteCommandWithOutput("GetThrottlerConfiguration", "--", "--server", server) - require.Nil(t, err) - for _, name := range names { - str := fmt.Sprintf("| %s | target_replication_lag_sec:12345 ", name) - assert.Contains(t, output, str) - assert.NotContains(t, output, "ignore_n_slowest_replicas") - } - assert.Contains(t, output, msg) - - // Reset clears our configuration values. - output, err = ci.VtctlclientProcess.ExecuteCommandWithOutput("ResetThrottlerConfiguration", "--", "--server", server) - require.Nil(t, err) - assert.Contains(t, output, msg) - - // Check that the reset configuration no longer has our values. - output, err = ci.VtctlclientProcess.ExecuteCommandWithOutput("GetThrottlerConfiguration", "--", "--server", server) - require.Nil(t, err) - assert.NotContains(t, output, "target_replication_lag_sec:12345") - assert.Contains(t, output, msg) - -} - -// CheckThrottlerService runs checkThrottlerServiceMaxRates and checkThrottlerServiceConfigs -func CheckThrottlerService(t *testing.T, server string, names []string, rate int, ci cluster.LocalProcessCluster) { - checkThrottlerServiceMaxRates(t, server, names, rate, ci) - checkThrottlerServiceConfiguration(t, server, names, ci) -} - -func getDBConnFromTablet(t *testing.T, vttablet *cluster.Vttablet, ks string) *mysql.Conn { - dbParams := cluster.NewConnParams(vttablet.VttabletProcess.DbPort, vttablet.VttabletProcess.DbPassword, path.Join(vttablet.VttabletProcess.Directory, "mysql.sock"), ks) - dbConn, err := mysql.Connect(context.Background(), &dbParams) - require.NoError(t, err) - return dbConn -} diff --git a/go/test/endtoend/sharding/initialsharding/bytes/initial_sharding_bytes_test.go b/go/test/endtoend/sharding/initialsharding/bytes/initial_sharding_bytes_test.go deleted file mode 100644 index 72794e88810..00000000000 --- a/go/test/endtoend/sharding/initialsharding/bytes/initial_sharding_bytes_test.go +++ /dev/null @@ -1,38 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - - -"""Re-runs initial_sharding_test.go with a varbinary keyspace_id.""" -*/ - -package bytes - -import ( - "testing" - - "vitess.io/vitess/go/test/endtoend/cluster" - sharding "vitess.io/vitess/go/test/endtoend/sharding/initialsharding" - querypb "vitess.io/vitess/go/vt/proto/query" -) - -func TestInitialShardingBytes(t *testing.T) { - defer cluster.PanicHandler(t) - code, err := sharding.ClusterWrapper(false) - if err != nil { - t.Errorf("setup failed with status code %d", code) - } - sharding.TestInitialSharding(t, &sharding.ClusterInstance.Keyspaces[0], querypb.Type_VARBINARY, false, false) - defer sharding.ClusterInstance.Teardown() -} diff --git a/go/test/endtoend/sharding/initialsharding/multi/initial_sharding_multi_test.go b/go/test/endtoend/sharding/initialsharding/multi/initial_sharding_multi_test.go deleted file mode 100644 index 06be7e12cc9..00000000000 --- a/go/test/endtoend/sharding/initialsharding/multi/initial_sharding_multi_test.go +++ /dev/null @@ -1,66 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - -This test simulates the first time a database has to be split -in a multi-vttablet-single-mysql environment - -We have 2 keyspaces. One keyspace is in managing mode. It's vttablets -own the MySQL instances and can reparent, start/stop server, start/stop -replication etc. Other keyspace is in non-managing mode and cannot do -any of these actions. Only TabletExternallyReparented is allowed, but -resharding should still work. - -For each keyspace: -- we start with a keyspace with a single shard and a single table -- we add and populate the sharding key -- we set the sharding key in the topology -- we clone into 2 instances -- we enable filtered replication -- we move all serving types -- we remove the source tablets -- we remove the original shard - -*/ - -package multi - -import ( - "testing" - - "vitess.io/vitess/go/vt/log" - - "vitess.io/vitess/go/test/endtoend/cluster" - sharding "vitess.io/vitess/go/test/endtoend/sharding/initialsharding" - querypb "vitess.io/vitess/go/vt/proto/query" -) - -func TestInitialShardingMulti(t *testing.T) { - defer cluster.PanicHandler(t) - code, err := sharding.ClusterWrapper(true) - if err != nil { - t.Errorf("setup failed with status code %d", code) - } - sharding.AssignMysqlPortFromKs1ToKs2() - sharding.TestInitialSharding(t, &sharding.ClusterInstance.Keyspaces[0], querypb.Type_UINT64, true, false) - log.Info("-----------------------------") - log.Info("Done with 1st keyspace test") - log.Info("-----------------------------") - sharding.TestInitialSharding(t, &sharding.ClusterInstance.Keyspaces[1], querypb.Type_UINT64, true, true) - log.Info("----------Done with 2nd keyspace test----------") - sharding.KillVtgateInstances() - sharding.KillTabletsInKeyspace(&sharding.ClusterInstance.Keyspaces[0]) - sharding.KillTabletsInKeyspace(&sharding.ClusterInstance.Keyspaces[1]) - defer sharding.ClusterInstance.Teardown() -} diff --git a/go/test/endtoend/sharding/initialsharding/sharding_util.go b/go/test/endtoend/sharding/initialsharding/sharding_util.go deleted file mode 100644 index f2a933d7bb7..00000000000 --- a/go/test/endtoend/sharding/initialsharding/sharding_util.go +++ /dev/null @@ -1,735 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package initialsharding - -import ( - "fmt" - "os" - "os/exec" - "path" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "vitess.io/vitess/go/test/endtoend/cluster" - "vitess.io/vitess/go/test/endtoend/sharding" - - querypb "vitess.io/vitess/go/vt/proto/query" - topodatapb "vitess.io/vitess/go/vt/proto/topodata" -) - -var ( - // ClusterInstance instance to be used for test with different params - ClusterInstance *cluster.LocalProcessCluster - hostname = "localhost" - keyspaceName1 = "ks1" - keyspaceName2 = "ks2" - dbPwd = "" - cell = "zone1" - newInitDbFile string - dbCredentialFile string - vtgateInstances []*cluster.VtgateProcess - commonTabletArg = []string{ - "--vreplication_healthcheck_topology_refresh", "1s", - "--vreplication_healthcheck_retry_delay", "1s", - "--vreplication_retry_delay", "1s", - "--degraded_threshold", "5s", - "--lock_tables_timeout", "5s", - "--watch_replication_stream", - "--enable_replication_reporter", - "--serving_state_grace_period", "1s", - "--binlog_use_v3_resharding_mode=true"} - createTabletTemplate = ` - create table %s( - msg varchar(64), - id bigint(20) unsigned, - primary key (id) - ) Engine=InnoDB; -` - createTabletTemplateByte = ` - create table %s( - msg varchar(64), - id varbinary(64), - primary key (id) - ) Engine=InnoDB; -` - insertTabletTemplate = `insert into %s(id, msg) values(%d, "%s")` - tableName = "resharding1" - vSchema = ` - { - "sharded": true, - "vindexes": { - "hash_index": { - "type": "hash" - } - }, - "tables": { - "%s": { - "column_vindexes": [ - { - "column": "%s", - "name": "hash_index" - } - ] - } - } - } - ` -) - -// ClusterWrapper common wrapper code for cluster -func ClusterWrapper(isMulti bool) (int, error) { - ClusterInstance = nil - ClusterInstance = cluster.NewCluster(cell, hostname) - - // Start topo server - if err := ClusterInstance.StartTopo(); err != nil { - return 1, err - } - - if isMulti { - WriteDbCredentialToTmp(ClusterInstance.TmpDirectory) - writeInitDBFile() - dbPwd = "VtDbaPass" - } - - if err := ClusterInstance.VtctlProcess.CreateKeyspace(keyspaceName1); err != nil { - return 1, err - } - ClusterInstance.Keyspaces = append(ClusterInstance.Keyspaces, cluster.Keyspace{Name: keyspaceName1}) - vtctldClientProcess := cluster.VtctldClientProcessInstance("localhost", ClusterInstance.VtctldProcess.GrpcPort, ClusterInstance.TmpDirectory) - if _, err := vtctldClientProcess.ExecuteCommandWithOutput("SetKeyspaceDurabilityPolicy", keyspaceName1, "--durability-policy=semi_sync"); err != nil { - return 1, err - } - - if isMulti { - if err := ClusterInstance.VtctlProcess.CreateKeyspace(keyspaceName2); err != nil { - return 1, err - } - ClusterInstance.Keyspaces = append(ClusterInstance.Keyspaces, cluster.Keyspace{Name: keyspaceName2}) - if _, err := vtctldClientProcess.ExecuteCommandWithOutput("SetKeyspaceDurabilityPolicy", keyspaceName2, "--durability-policy=semi_sync"); err != nil { - return 1, err - } - } - - initClusterForInitialSharding(keyspaceName1, []string{"0"}, 3, true, isMulti) - initClusterForInitialSharding(keyspaceName1, []string{"-80", "80-"}, 3, true, isMulti) - - if isMulti { - initClusterForInitialSharding(keyspaceName2, []string{"0"}, 3, true, isMulti) - initClusterForInitialSharding(keyspaceName2, []string{"-80", "80-"}, 3, true, isMulti) - } - return 0, nil -} - -func initClusterForInitialSharding(keyspaceName string, shardNames []string, totalTabletsRequired int, rdonly bool, isMulti bool) { - var mysqlProcesses []*exec.Cmd - var extraArgs []string - if isMulti { - extraArgs = []string{"--db-credentials-file", dbCredentialFile} - } - - for _, shardName := range shardNames { - shard := &cluster.Shard{ - Name: shardName, - } - - for i := 0; i < totalTabletsRequired; i++ { - // instantiate vttablet object with reserved ports - var tablet *cluster.Vttablet - if i == totalTabletsRequired-1 && rdonly { - tablet = ClusterInstance.NewVttabletInstance("rdonly", 0, "") - } else if i == 0 { - tablet = ClusterInstance.NewVttabletInstance("primary", 0, "") - } else { - tablet = ClusterInstance.NewVttabletInstance("replica", 0, "") - } - // Start Mysqlctl process - tablet.MysqlctlProcess = *cluster.MysqlCtlProcessInstance(tablet.TabletUID, tablet.MySQLPort, ClusterInstance.TmpDirectory) - - if isMulti { - tablet.MysqlctlProcess.InitDBFile = newInitDbFile - tablet.MysqlctlProcess.ExtraArgs = extraArgs - } - // Start Mysqlctl process, for multi keyspace we need only 1st keyspace sql procs, that is why this check is added - if keyspaceName == keyspaceName1 { - proc, err := tablet.MysqlctlProcess.StartProcess() - if err != nil { - return - } - mysqlProcesses = append(mysqlProcesses, proc) - } else { // Since we'll be using mysql procs of keyspace-1 for ks-2, resetting this to 0 - tablet.MysqlctlProcess.TabletUID = 0 - } - - // start vttablet process - tablet.VttabletProcess = cluster.VttabletProcessInstance( - tablet.HTTPPort, - tablet.GrpcPort, - tablet.TabletUID, - ClusterInstance.Cell, - shardName, - keyspaceName, - ClusterInstance.VtctldProcess.Port, - tablet.Type, - ClusterInstance.TopoProcess.Port, - ClusterInstance.Hostname, - ClusterInstance.TmpDirectory, - ClusterInstance.VtTabletExtraArgs, - ClusterInstance.EnableSemiSync, - ClusterInstance.DefaultCharset) - tablet.Alias = tablet.VttabletProcess.TabletPath - tablet.VttabletProcess.DbPassword = dbPwd - tablet.VttabletProcess.EnableSemiSync = true - tablet.VttabletProcess.SupportsBackup = false - shard.Vttablets = append(shard.Vttablets, tablet) - } - for idx, ks := range ClusterInstance.Keyspaces { - if ks.Name == keyspaceName { - ClusterInstance.Keyspaces[idx].Shards = append(ClusterInstance.Keyspaces[idx].Shards, *shard) - } - } - } - for _, proc := range mysqlProcesses { - proc.Wait() - } - -} - -// AssignMysqlPortFromKs1ToKs2 assigns mysql port of all tablets of ks1 to all corresponding tablets of ks2 -func AssignMysqlPortFromKs1ToKs2() { - portMap := map[string]int{} - for _, shard := range ClusterInstance.Keyspaces[0].Shards { - for _, tablet := range shard.Vttablets { - portMap[fmt.Sprintf("%s-%s", shard.Name, tablet.Type)] = tablet.MySQLPort - } - } - - for _, shard := range ClusterInstance.Keyspaces[1].Shards { - for idx, tablet := range shard.Vttablets { - port := portMap[fmt.Sprintf("%s-%s", shard.Name, tablet.Type)] - shard.Vttablets[idx].MySQLPort = port - shard.Vttablets[idx].VttabletProcess.DbPort = port - } - } -} - -// TestInitialSharding - main test which accepts different params for various test -func TestInitialSharding(t *testing.T, keyspace *cluster.Keyspace, keyType querypb.Type, isMulti bool, isExternal bool) { - defer cluster.PanicHandler(t) - if isExternal { - commonTabletArg = append(commonTabletArg, "--db_host", "127.0.0.1") - commonTabletArg = append(commonTabletArg, "--disable_active_reparents") - for _, shard := range keyspace.Shards { - for _, tablet := range shard.Vttablets { - tablet.VttabletProcess.ExtraArgs = append(tablet.VttabletProcess.ExtraArgs, "--db_port", fmt.Sprintf("%d", tablet.MySQLPort)) - tablet.VttabletProcess.DbPassword = dbPwd - } - } - } - if isMulti { - commonTabletArg = append(commonTabletArg, "--db-credentials-file", dbCredentialFile) - } - // Start the primary and rdonly of 1st shard - shard1 := keyspace.Shards[0] - keyspaceName := keyspace.Name - shard1Ks := fmt.Sprintf("%s/%s", keyspaceName, shard1.Name) - shard1Primary := *shard1.PrimaryTablet() - - if isExternal { - for _, tablet := range shard1.Vttablets { - _ = tablet.VttabletProcess.CreateDB(keyspaceName) - } - } - - // primary tablet start - shard1Primary.VttabletProcess.ExtraArgs = append(shard1Primary.VttabletProcess.ExtraArgs, commonTabletArg...) - shard1.Replica().VttabletProcess.ExtraArgs = append(shard1.Replica().VttabletProcess.ExtraArgs, commonTabletArg...) - shard1.Rdonly().VttabletProcess.ExtraArgs = append(shard1.Rdonly().VttabletProcess.ExtraArgs, commonTabletArg...) - - err := shard1Primary.VttabletProcess.Setup() - require.NoError(t, err) - - if isExternal { - shard1.Rdonly().VttabletProcess.ServingStatus = "SERVING" - shard1.Replica().VttabletProcess.ServingStatus = "SERVING" - } - err = shard1.Rdonly().VttabletProcess.Setup() - require.NoError(t, err) - err = shard1.Replica().VttabletProcess.Setup() - require.NoError(t, err) - - // reparent to make the tablets work - if !isExternal { - // reparent to make the tablets work - err = ClusterInstance.VtctlclientProcess.InitShardPrimary(keyspace.Name, shard1.Name, cell, shard1Primary.TabletUID) - require.NoError(t, err) - } else { - err = shard1.Replica().VttabletProcess.WaitForTabletStatus("SERVING") - require.NoError(t, err) - _, err = ClusterInstance.VtctlclientProcess.ExecuteCommandWithOutput("TabletExternallyReparented", shard1Primary.Alias) - require.NoError(t, err) - } - - err = shard1.Replica().VttabletProcess.WaitForTabletStatus("SERVING") - require.NoError(t, err) - err = shard1.Rdonly().VttabletProcess.WaitForTabletStatus("SERVING") - require.NoError(t, err) - for _, vttablet := range shard1.Vttablets { - assert.Equal(t, vttablet.VttabletProcess.GetTabletStatus(), "SERVING") - } - // create the tables and add startup values - sqlSchemaToApply := createTabletTemplate - if keyType == querypb.Type_VARBINARY { - sqlSchemaToApply = createTabletTemplateByte - } - err = ClusterInstance.VtctlclientProcess.ApplySchema(keyspaceName, fmt.Sprintf(sqlSchemaToApply, tableName)) - require.NoError(t, err) - - err = ClusterInstance.VtctlclientProcess.ApplyVSchema(keyspaceName, fmt.Sprintf(vSchema, tableName, "id")) - require.NoError(t, err) - _, err = shard1Primary.VttabletProcess.QueryTablet(fmt.Sprintf(insertTabletTemplate, tableName, uint64(0x1000000000000000), "msg1"), keyspaceName, true) - require.NoError(t, err) - _, err = shard1Primary.VttabletProcess.QueryTablet(fmt.Sprintf(insertTabletTemplate, tableName, uint64(0x9000000000000000), "msg2"), keyspaceName, true) - require.NoError(t, err) - _, err = shard1Primary.VttabletProcess.QueryTablet(fmt.Sprintf(insertTabletTemplate, tableName, uint64(0xD000000000000000), "msg3"), keyspaceName, true) - require.NoError(t, err) - - // reload schema on all tablets so we can query them - for _, vttablet := range shard1.Vttablets { - _ = ClusterInstance.VtctlclientProcess.ExecuteCommand("ReloadSchema", vttablet.Alias) - } - vtgateInstance := ClusterInstance.NewVtgateInstance() - vtgateInstance.MySQLServerSocketPath = path.Join(ClusterInstance.TmpDirectory, fmt.Sprintf("mysql-%s.sock", keyspaceName)) - vtgateInstance.ExtraArgs = []string{"--retry-count", fmt.Sprintf("%d", 2), "--tablet_protocol", "grpc", "--normalize_queries", "--tablet_refresh_interval", "2s"} - err = vtgateInstance.Setup() - vtgateInstances = append(vtgateInstances, vtgateInstance) - require.NoError(t, err) - - for _, tabletType := range []string{"primary", "replica", "rdonly"} { - if err = vtgateInstance.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.%s", keyspaceName, shard1.Name, tabletType), 1); err != nil { - assert.Fail(t, err.Error()) - } - } - - _ = ClusterInstance.VtctlclientProcess.ExecuteCommand("RebuildKeyspaceGraph", keyspaceName) - - // run a health check on source replica so it responds to discovery - err = ClusterInstance.VtctlclientProcess.ExecuteCommand("RunHealthCheck", shard1.Replica().Alias) - require.NoError(t, err) - - // create the split shards - shard21 := keyspace.Shards[1] - shard22 := keyspace.Shards[2] - - for _, shard := range []cluster.Shard{shard21, shard22} { - for idx, vttablet := range shard.Vttablets { - vttablet.VttabletProcess.ExtraArgs = append(vttablet.VttabletProcess.ExtraArgs, commonTabletArg...) - if isExternal { - // We have to InitTablet upfront so that TER below can find the tablet record. - // Otherwise, there is a race where TER runs before vttablet publishes its first record. - err = ClusterInstance.VtctlclientProcess.InitTablet(vttablet, cell, keyspaceName, hostname, shard.Name) - require.Nil(t, err) - - err = vttablet.VttabletProcess.CreateDB(keyspaceName) - require.NoError(t, err) - shard.Vttablets[idx].VttabletProcess.ServingStatus = "" - } - err = vttablet.VttabletProcess.Setup() - require.NoError(t, err) - } - } - if !isExternal { - _ = ClusterInstance.VtctlclientProcess.InitShardPrimary(keyspaceName, shard21.Name, cell, shard21.PrimaryTablet().TabletUID) - _ = ClusterInstance.VtctlclientProcess.InitShardPrimary(keyspaceName, shard22.Name, cell, shard22.PrimaryTablet().TabletUID) - _ = ClusterInstance.VtctlclientProcess.ApplySchema(keyspaceName, fmt.Sprintf(sqlSchemaToApply, tableName)) - _ = ClusterInstance.VtctlclientProcess.ApplyVSchema(keyspaceName, fmt.Sprintf(vSchema, tableName, "id")) - - for _, shard := range []cluster.Shard{shard21, shard22} { - _ = shard.Replica().VttabletProcess.WaitForTabletStatus("SERVING") - _ = shard.Rdonly().VttabletProcess.WaitForTabletStatus("SERVING") - } - - for _, shard := range []cluster.Shard{shard21, shard22} { - for _, vttablet := range shard.Vttablets { - assert.Equal(t, vttablet.VttabletProcess.GetTabletStatus(), "SERVING") - } - } - } else { - _, err = ClusterInstance.VtctlclientProcess.ExecuteCommandWithOutput("TabletExternallyReparented", shard21.PrimaryTablet().Alias) - require.NoError(t, err) - _, err = ClusterInstance.VtctlclientProcess.ExecuteCommandWithOutput("TabletExternallyReparented", shard22.PrimaryTablet().Alias) - require.NoError(t, err) - } - - // must restart vtgate after tablets are up, or else wait until 1min refresh - // we want cache_ttl at zero so we re-read the topology for every test query. - - _ = vtgateInstance.TearDown() - _ = vtgateInstance.Setup() - - // Wait for the endpoints, either local or remote. - for _, shard := range []cluster.Shard{shard1, shard21, shard22} { - err = vtgateInstance.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.primary", keyspaceName, shard.Name), 1) - require.NoError(t, err) - err = vtgateInstance.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.replica", keyspaceName, shard.Name), 1) - require.NoError(t, err) - err = vtgateInstance.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.rdonly", keyspaceName, shard.Name), 1) - require.NoError(t, err) - } - - // Check srv keyspace - expectedPartitions := map[topodatapb.TabletType][]string{} - expectedPartitions[topodatapb.TabletType_PRIMARY] = []string{shard1.Name} - expectedPartitions[topodatapb.TabletType_REPLICA] = []string{shard1.Name} - expectedPartitions[topodatapb.TabletType_RDONLY] = []string{shard1.Name} - checkSrvKeyspaceForSharding(t, keyspaceName, expectedPartitions) - - err = ClusterInstance.VtctlclientProcess.ExecuteCommand("CopySchemaShard", "--", - "--exclude_tables", "unrelated", - shard1.Rdonly().Alias, fmt.Sprintf("%s/%s", keyspaceName, shard21.Name)) - require.NoError(t, err) - - err = ClusterInstance.VtctlclientProcess.ExecuteCommand("CopySchemaShard", "--", - "--exclude_tables", "unrelated", - shard1.Rdonly().Alias, fmt.Sprintf("%s/%s", keyspaceName, shard22.Name)) - require.NoError(t, err) - - err = ClusterInstance.StartVtworker(cell, "--use_v3_resharding_mode=true") - require.NoError(t, err) - - // Initial clone (online). - _ = ClusterInstance.VtworkerProcess.ExecuteCommand("SplitClone", "--", - "--offline=false", - "--exclude_tables", "unrelated", - "--chunk_count", "10", - "--min_rows_per_chunk", "1", - "--min_healthy_rdonly_tablets", "1", - fmt.Sprintf("%s/%s", keyspaceName, shard1.Name)) - - // Reset vtworker such that we can run the next command. - _ = ClusterInstance.VtworkerProcess.ExecuteCommand("Reset") - - // Modify the destination shard. SplitClone will revert the changes. - // Delete row 1 (provokes an insert). - _, _ = shard21.PrimaryTablet().VttabletProcess.QueryTablet(fmt.Sprintf("delete from %s where id=%d", tableName, uint64(0x1000000000000000)), keyspaceName, true) - // Delete row 2 (provokes an insert). - _, _ = shard22.PrimaryTablet().VttabletProcess.QueryTablet(fmt.Sprintf("delete from %s where id=%d", tableName, uint64(0x9000000000000000)), keyspaceName, true) - // Update row 3 (provokes an update). - _, _ = shard22.PrimaryTablet().VttabletProcess.QueryTablet(fmt.Sprintf("update %s set msg='msg-not-3' where id=%d", tableName, uint64(0xD000000000000000)), keyspaceName, true) - // Insert row 4 (provokes a delete). - var ksid uint64 = 0xD000000000000000 - insertSQL := fmt.Sprintf(sharding.InsertTabletTemplateKsID, tableName, ksid, "msg4", ksid) - sharding.ExecuteOnTablet(t, insertSQL, *shard22.PrimaryTablet(), keyspaceName, true) - - _ = ClusterInstance.VtworkerProcess.ExecuteCommand("SplitClone", "--", - "--exclude_tables", "unrelated", - "--chunk_count", "10", - "--min_rows_per_chunk", "1", - "--min_healthy_rdonly_tablets", "1", - fmt.Sprintf("%s/%s", keyspaceName, shard1.Name)) - - // check first value is in the left shard - for _, tablet := range shard21.Vttablets { - sharding.CheckValues(t, *tablet, 0x1000000000000000, "msg1", true, tableName, keyspaceName, keyType, nil) - } - - for _, tablet := range shard22.Vttablets { - sharding.CheckValues(t, *tablet, 0x1000000000000000, "msg1", false, tableName, keyspaceName, keyType, nil) - } - - for _, tablet := range shard21.Vttablets { - sharding.CheckValues(t, *tablet, 0x9000000000000000, "msg2", false, tableName, keyspaceName, keyType, nil) - } - - for _, tablet := range shard22.Vttablets { - sharding.CheckValues(t, *tablet, 0x9000000000000000, "msg2", true, tableName, keyspaceName, keyType, nil) - } - - for _, tablet := range shard21.Vttablets { - sharding.CheckValues(t, *tablet, 0xD000000000000000, "msg3", false, tableName, keyspaceName, keyType, nil) - } - - for _, tablet := range shard22.Vttablets { - sharding.CheckValues(t, *tablet, 0xD000000000000000, "msg3", true, tableName, keyspaceName, keyType, nil) - } - - err = ClusterInstance.VtctlclientProcess.ExecuteCommand("ValidateSchemaKeyspace", keyspaceName) - require.NoError(t, err) - - // check the binlog players are running - sharding.CheckDestinationPrimary(t, *shard21.PrimaryTablet(), []string{shard1Ks}, *ClusterInstance) - sharding.CheckDestinationPrimary(t, *shard22.PrimaryTablet(), []string{shard1Ks}, *ClusterInstance) - - // check that binlog server exported the stats vars - sharding.CheckBinlogServerVars(t, *shard1.Replica(), 0, 0, false) - - for _, tablet := range []cluster.Vttablet{*shard21.Rdonly(), *shard22.Rdonly()} { - err = ClusterInstance.VtctlclientProcess.ExecuteCommand("RunHealthCheck", tablet.Alias) - require.NoError(t, err) - } - - // testing filtered replication: insert a bunch of data on shard 1, - // check we get most of it after a few seconds, wait for binlog server - // timeout, check we get all of it. - sharding.InsertLots(t, 1000, shard1Primary, tableName, keyspaceName) - - assert.True(t, sharding.CheckLotsTimeout(t, *shard21.Replica(), 1000, tableName, keyspaceName, keyType, 49)) - assert.True(t, sharding.CheckLotsTimeout(t, *shard22.Replica(), 1000, tableName, keyspaceName, keyType, 51)) - - sharding.CheckDestinationPrimary(t, *shard21.PrimaryTablet(), []string{shard1Ks}, *ClusterInstance) - sharding.CheckDestinationPrimary(t, *shard22.PrimaryTablet(), []string{shard1Ks}, *ClusterInstance) - sharding.CheckBinlogServerVars(t, *shard1.Replica(), 1000, 1000, false) - - err = ClusterInstance.VtctlclientProcess.ExecuteCommand("RunHealthCheck", shard21.Rdonly().Alias) - require.NoError(t, err) - err = ClusterInstance.VtctlclientProcess.ExecuteCommand("RunHealthCheck", shard22.Rdonly().Alias) - require.NoError(t, err) - - //use vtworker to compare the data - ClusterInstance.VtworkerProcess.Cell = cell - if !isMulti { - err = ClusterInstance.VtworkerProcess.ExecuteVtworkerCommand(ClusterInstance.GetAndReservePort(), - ClusterInstance.GetAndReservePort(), - "--use_v3_resharding_mode=true", - "MultiSplitDiff", - fmt.Sprintf("%s/%s", keyspaceName, shard1.Name)) - require.NoError(t, err) - - for _, shard := range []string{shard21.Name, shard22.Name} { - err = ClusterInstance.VtworkerProcess.ExecuteVtworkerCommand(ClusterInstance.GetAndReservePort(), - ClusterInstance.GetAndReservePort(), - "--use_v3_resharding_mode=true", - "SplitDiff", "--", - "--min_healthy_rdonly_tablets", "1", - fmt.Sprintf("%s/%s", keyspaceName, shard)) - require.NoError(t, err) - } - } - - // check we can't migrate the primary just yet - err = ClusterInstance.VtctlclientProcess.ExecuteCommand("MigrateServedTypes", shard1Ks, "primary") - require.Error(t, err) - - // now serve rdonly from the split shards - err = ClusterInstance.VtctlclientProcess.ExecuteCommand("MigrateServedTypes", shard1Ks, "rdonly") - require.NoError(t, err) - expectedPartitions = map[topodatapb.TabletType][]string{} - expectedPartitions[topodatapb.TabletType_PRIMARY] = []string{shard1.Name} - expectedPartitions[topodatapb.TabletType_REPLICA] = []string{shard1.Name} - expectedPartitions[topodatapb.TabletType_RDONLY] = []string{shard21.Name, shard22.Name} - checkSrvKeyspaceForSharding(t, keyspaceName, expectedPartitions) - - _ = shard21.Rdonly().VttabletProcess.WaitForTabletStatus("SERVING") - _ = shard22.Rdonly().VttabletProcess.WaitForTabletStatus("SERVING") - - _ = vtgateInstance.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.rdonly", keyspaceName, shard21.Name), 1) - _ = vtgateInstance.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.rdonly", keyspaceName, shard22.Name), 1) - - //then serve replica from the split shards - - sourceTablet := shard1.Replica() - destinationTablets := []cluster.Vttablet{*shard21.Replica(), *shard22.Replica()} - - _ = ClusterInstance.VtctlclientProcess.ExecuteCommand("MigrateServedTypes", shard1Ks, "replica") - expectedPartitions = map[topodatapb.TabletType][]string{} - expectedPartitions[topodatapb.TabletType_PRIMARY] = []string{shard1.Name} - expectedPartitions[topodatapb.TabletType_REPLICA] = []string{shard21.Name, shard22.Name} - expectedPartitions[topodatapb.TabletType_RDONLY] = []string{shard21.Name, shard22.Name} - checkSrvKeyspaceForSharding(t, keyspaceName, expectedPartitions) - - //move replica back and forth - _ = ClusterInstance.VtctlclientProcess.ExecuteCommand("MigrateServedTypes", "--", "--reverse", shard1Ks, "replica") - - // After a backwards migration, queryservice should be enabled on source and disabled on destinations - sharding.CheckTabletQueryService(t, *sourceTablet, "SERVING", false, *ClusterInstance) - sharding.CheckTabletQueryServices(t, destinationTablets, "NOT_SERVING", true, *ClusterInstance) - - expectedPartitions = map[topodatapb.TabletType][]string{} - expectedPartitions[topodatapb.TabletType_PRIMARY] = []string{shard1.Name} - expectedPartitions[topodatapb.TabletType_REPLICA] = []string{shard1.Name} - expectedPartitions[topodatapb.TabletType_RDONLY] = []string{shard21.Name, shard22.Name} - checkSrvKeyspaceForSharding(t, keyspaceName, expectedPartitions) - - _ = ClusterInstance.VtctlclientProcess.ExecuteCommand("MigrateServedTypes", shard1Ks, "replica") - - // After a forwards migration, queryservice should be disabled on source and enabled on destinations - sharding.CheckTabletQueryService(t, *sourceTablet, "NOT_SERVING", true, *ClusterInstance) - sharding.CheckTabletQueryServices(t, destinationTablets, "SERVING", false, *ClusterInstance) - expectedPartitions = map[topodatapb.TabletType][]string{} - expectedPartitions[topodatapb.TabletType_PRIMARY] = []string{shard1.Name} - expectedPartitions[topodatapb.TabletType_REPLICA] = []string{shard21.Name, shard22.Name} - expectedPartitions[topodatapb.TabletType_RDONLY] = []string{shard21.Name, shard22.Name} - checkSrvKeyspaceForSharding(t, keyspaceName, expectedPartitions) - - // then serve primary from the split shards - _ = ClusterInstance.VtctlclientProcess.ExecuteCommand("MigrateServedTypes", shard1Ks, "primary") - expectedPartitions = map[topodatapb.TabletType][]string{} - expectedPartitions[topodatapb.TabletType_PRIMARY] = []string{shard21.Name, shard22.Name} - expectedPartitions[topodatapb.TabletType_REPLICA] = []string{shard21.Name, shard22.Name} - expectedPartitions[topodatapb.TabletType_RDONLY] = []string{shard21.Name, shard22.Name} - checkSrvKeyspaceForSharding(t, keyspaceName, expectedPartitions) - - // check the binlog players are gone now - err = shard21.PrimaryTablet().VttabletProcess.WaitForBinLogPlayerCount(0) - require.NoError(t, err) - err = shard22.PrimaryTablet().VttabletProcess.WaitForBinLogPlayerCount(0) - require.NoError(t, err) - - // make sure we can't delete a shard with tablets - err = ClusterInstance.VtctlclientProcess.ExecuteCommand("DeleteShard", shard1Ks) - require.Error(t, err) - ClusterInstance.VtworkerProcess.TearDown() - if !isMulti { - KillVtgateInstances() - KillTabletsInKeyspace(keyspace) - } - -} - -// KillTabletsInKeyspace kill the first shard tablets in ordered way -func KillTabletsInKeyspace(keyspace *cluster.Keyspace) { - // Teardown - shard1 := keyspace.Shards[0] - var mysqlctlProcessList []*exec.Cmd - for _, tablet := range []cluster.Vttablet{*shard1.PrimaryTablet(), *shard1.Replica(), *shard1.Rdonly()} { - proc, _ := tablet.MysqlctlProcess.StopProcess() - mysqlctlProcessList = append(mysqlctlProcessList, proc) - _ = tablet.VttabletProcess.TearDown() - } - for _, proc := range mysqlctlProcessList { - proc.Wait() - } - _ = ClusterInstance.VtctlclientProcess.ExecuteCommand("DeleteTablet", shard1.Replica().Alias) - _ = ClusterInstance.VtctlclientProcess.ExecuteCommand("DeleteTablet", shard1.Rdonly().Alias) - _ = ClusterInstance.VtctlclientProcess.ExecuteCommand("DeleteTablet", "--", "--allow_primary", shard1.PrimaryTablet().Alias) - - _ = ClusterInstance.VtctlclientProcess.ExecuteCommand("RebuildKeyspaceGraph", keyspace.Name) - _ = ClusterInstance.VtctlclientProcess.ExecuteCommand("DeleteShard", keyspace.Name+"/"+shard1.Name) -} - -// KillVtgateInstances stops the vtgate process -func KillVtgateInstances() { - if len(vtgateInstances) > 0 { - for _, vtgateInstance := range vtgateInstances { - _ = vtgateInstance.TearDown() - } - } -} - -func checkSrvKeyspaceForSharding(t *testing.T, ksName string, expectedPartitions map[topodatapb.TabletType][]string) { - sharding.CheckSrvKeyspace(t, cell, ksName, expectedPartitions, *ClusterInstance) -} - -// Create a new init_db.sql file that sets up passwords for all users. -// Then we use a db-credentials-file with the passwords. -func writeInitDBFile() { - initDb, _ := os.ReadFile(path.Join(os.Getenv("VTROOT"), "/config/init_db.sql")) - sql := string(initDb) - newInitDbFile = path.Join(ClusterInstance.TmpDirectory, "init_db_with_passwords.sql") - sql = sql + GetPasswordUpdateSQL(ClusterInstance) + ` -# connecting through a port requires 127.0.0.1 -# --host=localhost will connect through socket -CREATE USER 'vt_dba'@'127.0.0.1' IDENTIFIED BY 'VtDbaPass'; -GRANT ALL ON *.* TO 'vt_dba'@'127.0.0.1'; -GRANT GRANT OPTION ON *.* TO 'vt_dba'@'127.0.0.1'; -# User for app traffic, with global read-write access. -CREATE USER 'vt_app'@'127.0.0.1' IDENTIFIED BY 'VtAppPass'; -GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, RELOAD, PROCESS, FILE, - REFERENCES, INDEX, ALTER, SHOW DATABASES, CREATE TEMPORARY TABLES, - LOCK TABLES, EXECUTE, REPLICATION CLIENT, CREATE VIEW, SHOW VIEW, - CREATE ROUTINE, ALTER ROUTINE, CREATE USER, EVENT, TRIGGER - ON *.* TO 'vt_app'@'127.0.0.1'; -# User for administrative operations that need to be executed as non-SUPER. -# Same permissions as vt_app here. -CREATE USER 'vt_allprivs'@'127.0.0.1' IDENTIFIED BY 'VtAllPrivsPass'; -GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, RELOAD, PROCESS, FILE, - REFERENCES, INDEX, ALTER, SHOW DATABASES, CREATE TEMPORARY TABLES, - LOCK TABLES, EXECUTE, REPLICATION SLAVE, REPLICATION CLIENT, CREATE VIEW, - SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, CREATE USER, EVENT, TRIGGER - ON *.* TO 'vt_allprivs'@'127.0.0.1'; -# User for Vitess VReplication (base vstreamers and vplayer). -CREATE USER 'vt_filtered'@'127.0.0.1' IDENTIFIED BY 'VtFilteredPass'; -GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, RELOAD, PROCESS, FILE, - REFERENCES, INDEX, ALTER, SHOW DATABASES, CREATE TEMPORARY TABLES, - LOCK TABLES, EXECUTE, REPLICATION SLAVE, REPLICATION CLIENT, CREATE VIEW, - SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, CREATE USER, EVENT, TRIGGER - ON *.* TO 'vt_filtered'@'127.0.0.1'; -FLUSH PRIVILEGES; -` - os.WriteFile(newInitDbFile, []byte(sql), 0666) - -} - -// WriteDbCredentialToTmp writes json format db credentials to tmp directory -func WriteDbCredentialToTmp(tmpDir string) string { - data := []byte(`{ - "vt_dba": ["VtDbaPass"], - "vt_app": ["VtAppPass"], - "vt_allprivs": ["VtAllprivsPass"], - "vt_repl": ["VtReplPass"], - "vt_filtered": ["VtFilteredPass"] - }`) - dbCredentialFile = path.Join(tmpDir, "db_credentials.json") - os.WriteFile(dbCredentialFile, data, 0666) - return dbCredentialFile -} - -// GetPasswordUpdateSQL returns the sql for password update -func GetPasswordUpdateSQL(localCluster *cluster.LocalProcessCluster) string { - pwdChangeCmd := ` - # Set real passwords for all users. - UPDATE mysql.user SET %s = PASSWORD('RootPass') - WHERE User = 'root' AND Host = 'localhost'; - UPDATE mysql.user SET %s = PASSWORD('VtDbaPass') - WHERE User = 'vt_dba' AND Host = 'localhost'; - UPDATE mysql.user SET %s = PASSWORD('VtAppPass') - WHERE User = 'vt_app' AND Host = 'localhost'; - UPDATE mysql.user SET %s = PASSWORD('VtAllprivsPass') - WHERE User = 'vt_allprivs' AND Host = 'localhost'; - UPDATE mysql.user SET %s = PASSWORD('VtReplPass') - WHERE User = 'vt_repl' AND Host = '%%'; - UPDATE mysql.user SET %s = PASSWORD('VtFilteredPass') - WHERE User = 'vt_filtered' AND Host = 'localhost'; - FLUSH PRIVILEGES; - ` - pwdCol, _ := getPasswordField(localCluster) - return fmt.Sprintf(pwdChangeCmd, pwdCol, pwdCol, pwdCol, pwdCol, pwdCol, pwdCol) -} - -// getPasswordField Determines which column is used for user passwords in this MySQL version. -func getPasswordField(localCluster *cluster.LocalProcessCluster) (pwdCol string, err error) { - tablet := &cluster.Vttablet{ - Type: "relpica", - TabletUID: 100, - MySQLPort: 15000, - MysqlctlProcess: *cluster.MysqlCtlProcessInstance(100, 15000, localCluster.TmpDirectory), - } - if err = tablet.MysqlctlProcess.Start(); err != nil { - return "", err - } - tablet.VttabletProcess = cluster.VttabletProcessInstance(tablet.HTTPPort, tablet.GrpcPort, tablet.TabletUID, "", "", "", 0, tablet.Type, localCluster.TopoPort, "", "", nil, false, localCluster.DefaultCharset) - result, err := tablet.VttabletProcess.QueryTablet("select password from mysql.user limit 0", "", false) - if err == nil && len(result.Rows) > 0 { - return "password", nil - } - tablet.MysqlctlProcess.Stop() - os.RemoveAll(path.Join(tablet.VttabletProcess.Directory)) - return "authentication_string", nil - -} diff --git a/go/test/endtoend/sharding/initialsharding/v3/initial_sharding_test.go b/go/test/endtoend/sharding/initialsharding/v3/initial_sharding_test.go deleted file mode 100644 index 95e5e5c8975..00000000000 --- a/go/test/endtoend/sharding/initialsharding/v3/initial_sharding_test.go +++ /dev/null @@ -1,47 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - -This test simulates the first time a database has to be split. - -- we start with a keyspace with a single shard and a single table -- we add and populate the sharding key -- we set the sharding key in the topology -- we clone into 2 instances -- we enable filtered replication -- we move all serving types -- we remove the source tablets -- we remove the original shard - -*/ - -package v3 - -import ( - "testing" - - "vitess.io/vitess/go/test/endtoend/cluster" - sharding "vitess.io/vitess/go/test/endtoend/sharding/initialsharding" - querypb "vitess.io/vitess/go/vt/proto/query" -) - -func TestInitialSharding(t *testing.T) { - defer cluster.PanicHandler(t) - code, err := sharding.ClusterWrapper(false) - if err != nil { - t.Errorf("setup failed with status code %d", code) - } - sharding.TestInitialSharding(t, &sharding.ClusterInstance.Keyspaces[0], querypb.Type_UINT64, false, false) - defer sharding.ClusterInstance.Teardown() -} diff --git a/go/test/endtoend/sharding/mergesharding/int/mergesharding_int_test.go b/go/test/endtoend/sharding/mergesharding/int/mergesharding_int_test.go deleted file mode 100644 index 94efd03bf4e..00000000000 --- a/go/test/endtoend/sharding/mergesharding/int/mergesharding_int_test.go +++ /dev/null @@ -1,29 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v3 - -import ( - "testing" - - sharding "vitess.io/vitess/go/test/endtoend/sharding/mergesharding" -) - -// TestMergeShardingIntShardingKey - tests merge sharding using a INT column -func TestMergeShardingIntShardingKey(t *testing.T) { - sharding.TestMergesharding(t, false /* useVarbinaryShardingKeyType */) - -} diff --git a/go/test/endtoend/sharding/mergesharding/mergesharding_base.go b/go/test/endtoend/sharding/mergesharding/mergesharding_base.go deleted file mode 100644 index 84a40674f9c..00000000000 --- a/go/test/endtoend/sharding/mergesharding/mergesharding_base.go +++ /dev/null @@ -1,626 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package mergesharding - -import ( - "context" - "encoding/json" - "fmt" - "os/exec" - "path" - "strings" - "sync" - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "vitess.io/vitess/go/mysql" - "vitess.io/vitess/go/sqltypes" - "vitess.io/vitess/go/test/endtoend/cluster" - "vitess.io/vitess/go/test/endtoend/sharding" - "vitess.io/vitess/go/vt/log" - - querypb "vitess.io/vitess/go/vt/proto/query" - topodatapb "vitess.io/vitess/go/vt/proto/topodata" -) - -var ( - // ClusterInstance instance to be used for test with different params - clusterInstance *cluster.LocalProcessCluster - hostname = "localhost" - keyspaceName = "ks" - cell = "zone1" - createTabletTemplate = ` - create table %s( - custom_ksid_col %s not null, - msg varchar(64), - id bigint not null, - parent_id bigint not null, - primary key (parent_id, id), - index by_msg (msg) - ) Engine=InnoDB; - ` - fixedParentID = 86 - tableName = "resharding1" - vSchema = ` - { - "sharded": true, - "vindexes": { - "hash_index": { - "type": "hash" - } - }, - "tables": { - "resharding1": { - "column_vindexes": [ - { - "column": "custom_ksid_col", - "name": "hash_index" - } - ] - } - } - } - ` - // insertTabletTemplateKsID common insert format - insertTabletTemplateKsID = `insert into %s (parent_id, id, msg, custom_ksid_col) values (%d, %d, '%s', %d) /* vtgate:: keyspace_id:%d */ /* id:%d */` - - // initial shards - // range -40, 40-80 & 80- - shard0 = &cluster.Shard{Name: "-40"} - shard1 = &cluster.Shard{Name: "40-80"} - shard2 = &cluster.Shard{Name: "80-"} - - // merge shard - // merging -40 & 40-80 to -80 - shard3 = &cluster.Shard{Name: "-80"} - - // Sharding keys - key1 uint64 = 1 // Key redirect to shard 0 [-40] - key2 uint64 = 3 // key redirect to shard 1 [40-80] - key3 uint64 = 4 // Key redirect to shard 2 [80-] -) - -// TestMergesharding covers the workflow for a sharding merge. -// We start with 3 shards: -40, 40-80, and 80-. We then merge -40 and 40-80 into -80. -// Note this test is just testing the full workflow, not corner cases or error -// cases. These are mostly done by the other resharding tests. -func TestMergesharding(t *testing.T, useVarbinaryShardingKeyType bool) { - defer cluster.PanicHandler(t) - clusterInstance = cluster.NewCluster(cell, hostname) - defer clusterInstance.Teardown() - - // Launch keyspace - keyspace := &cluster.Keyspace{Name: keyspaceName} - - // Start topo server - err := clusterInstance.StartTopo() - require.NoError(t, err) - - // Defining all the tablets - shard0Primary := clusterInstance.NewVttabletInstance("replica", 0, "") - shard0Replica := clusterInstance.NewVttabletInstance("replica", 0, "") - shard0Rdonly := clusterInstance.NewVttabletInstance("rdonly", 0, "") - - shard1Primary := clusterInstance.NewVttabletInstance("replica", 0, "") - shard1Replica := clusterInstance.NewVttabletInstance("replica", 0, "") - shard1Rdonly := clusterInstance.NewVttabletInstance("rdonly", 0, "") - - shard2Primary := clusterInstance.NewVttabletInstance("replica", 0, "") - shard2Replica := clusterInstance.NewVttabletInstance("replica", 0, "") - shard2Rdonly := clusterInstance.NewVttabletInstance("rdonly", 0, "") - - shard3Primary := clusterInstance.NewVttabletInstance("replica", 0, "") - shard3Replica := clusterInstance.NewVttabletInstance("replica", 0, "") - shard3Rdonly := clusterInstance.NewVttabletInstance("rdonly", 0, "") - - shard0.Vttablets = []*cluster.Vttablet{shard0Primary, shard0Replica, shard0Rdonly} - shard1.Vttablets = []*cluster.Vttablet{shard1Primary, shard1Replica, shard1Rdonly} - shard2.Vttablets = []*cluster.Vttablet{shard2Primary, shard2Replica, shard2Rdonly} - shard3.Vttablets = []*cluster.Vttablet{shard3Primary, shard3Replica, shard3Rdonly} - - clusterInstance.VtTabletExtraArgs = []string{ - "--vreplication_healthcheck_topology_refresh", "1s", - "--vreplication_healthcheck_retry_delay", "1s", - "--vreplication_retry_delay", "1s", - "--degraded_threshold", "5s", - "--lock_tables_timeout", "5s", - "--watch_replication_stream", - "--enable_semi_sync", - "--enable_replication_reporter", - "--enable-tx-throttler", - "--binlog_use_v3_resharding_mode=true", - } - - shardingColumnType := "bigint(20) unsigned" - shardingKeyType := querypb.Type_UINT64 - - if useVarbinaryShardingKeyType { - shardingColumnType = "varbinary(64)" - shardingKeyType = querypb.Type_VARBINARY - } - - // Initialize Cluster - err = clusterInstance.SetupCluster(keyspace, []cluster.Shard{*shard0, *shard1, *shard2, *shard3}) - require.NoError(t, err) - assert.Equal(t, len(clusterInstance.Keyspaces[0].Shards), 4) - - vtctldClientProcess := cluster.VtctldClientProcessInstance("localhost", clusterInstance.VtctldProcess.GrpcPort, clusterInstance.TmpDirectory) - out, err := vtctldClientProcess.ExecuteCommandWithOutput("SetKeyspaceDurabilityPolicy", keyspaceName, "--durability-policy=semi_sync") - require.NoError(t, err, out) - - //Start MySql - var mysqlCtlProcessList []*exec.Cmd - for _, shard := range clusterInstance.Keyspaces[0].Shards { - for _, tablet := range shard.Vttablets { - log.Infof("Starting MySql for tablet %v", tablet.Alias) - if proc, err := tablet.MysqlctlProcess.StartProcess(); err != nil { - t.Fatal(err) - } else { - mysqlCtlProcessList = append(mysqlCtlProcessList, proc) - } - } - } - - // Wait for mysql processes to start - for _, proc := range mysqlCtlProcessList { - if err := proc.Wait(); err != nil { - t.Fatal(err) - } - } - - // Rebuild keyspace Graph - err = clusterInstance.VtctlclientProcess.ExecuteCommand("RebuildKeyspaceGraph", keyspaceName) - require.NoError(t, err) - - //Start Tablets and Wait for the Process - for _, shard := range clusterInstance.Keyspaces[0].Shards { - for _, tablet := range shard.Vttablets { - err = tablet.VttabletProcess.Setup() - require.NoError(t, err) - } - } - - // Init Shard primary - err = clusterInstance.VtctlclientProcess.InitializeShard(keyspaceName, shard0.Name, shard0Primary.Cell, shard0Primary.TabletUID) - require.NoError(t, err) - err = clusterInstance.VtctlclientProcess.InitializeShard(keyspaceName, shard1.Name, shard1Primary.Cell, shard1Primary.TabletUID) - require.NoError(t, err) - - err = clusterInstance.VtctlclientProcess.InitializeShard(keyspaceName, shard2.Name, shard2Primary.Cell, shard2Primary.TabletUID) - require.NoError(t, err) - - // Init Shard primary on Merge Shard - err = clusterInstance.VtctlclientProcess.InitializeShard(keyspaceName, shard3.Name, shard3Primary.Cell, shard3Primary.TabletUID) - require.NoError(t, err) - - // Wait for tablets to come in Service state - err = shard0Primary.VttabletProcess.WaitForTabletStatus("SERVING") - require.NoError(t, err) - err = shard1Primary.VttabletProcess.WaitForTabletStatus("SERVING") - require.NoError(t, err) - err = shard2Primary.VttabletProcess.WaitForTabletStatus("SERVING") - require.NoError(t, err) - err = shard3Primary.VttabletProcess.WaitForTabletStatus("SERVING") - require.NoError(t, err) - - // keyspace/shard name fields - shard0Ks := fmt.Sprintf("%s/%s", keyspaceName, shard0.Name) - shard1Ks := fmt.Sprintf("%s/%s", keyspaceName, shard1.Name) - shard3Ks := fmt.Sprintf("%s/%s", keyspaceName, shard3.Name) - - // check for shards - result, err := clusterInstance.VtctlclientProcess.ExecuteCommandWithOutput("FindAllShardsInKeyspace", keyspaceName) - require.NoError(t, err) - resultMap := make(map[string]any) - err = json.Unmarshal([]byte(result), &resultMap) - require.NoError(t, err) - assert.Equal(t, 4, len(resultMap), "No of shards should be 4") - - // Apply Schema - err = clusterInstance.VtctlclientProcess.ApplySchema(keyspaceName, fmt.Sprintf(createTabletTemplate, "resharding1", shardingColumnType)) - require.NoError(t, err) - - // Apply VSchema - err = clusterInstance.VtctlclientProcess.ApplyVSchema(keyspaceName, vSchema) - require.NoError(t, err) - - // Insert Data - insertStartupValues(t) - - // run a health check on source replicas so they respond to discovery - // (for binlog players) and on the source rdonlys (for workers) - for _, shard := range keyspace.Shards { - for _, tablet := range shard.Vttablets { - err = clusterInstance.VtctlclientProcess.ExecuteCommand("RunHealthCheck", tablet.Alias) - require.NoError(t, err) - } - } - - // Rebuild keyspace Graph - err = clusterInstance.VtctlclientProcess.ExecuteCommand("RebuildKeyspaceGraph", keyspaceName) - require.NoError(t, err) - - // check srv keyspace - expectedPartitions := map[topodatapb.TabletType][]string{} - expectedPartitions[topodatapb.TabletType_PRIMARY] = []string{shard0.Name, shard1.Name, shard2.Name} - expectedPartitions[topodatapb.TabletType_REPLICA] = []string{shard0.Name, shard1.Name, shard2.Name} - expectedPartitions[topodatapb.TabletType_RDONLY] = []string{shard0.Name, shard1.Name, shard2.Name} - sharding.CheckSrvKeyspace(t, cell, keyspaceName, expectedPartitions, *clusterInstance) - - // we need to create the schema, and the worker will do data copying - err = clusterInstance.VtctlclientProcess.ExecuteCommand("CopySchemaShard", - shard0.Rdonly().Alias, fmt.Sprintf("%s/%s", keyspaceName, shard3.Name)) - require.NoError(t, err) - - // Run vtworker as daemon for the following SplitClone commands. --use_v3_resharding_mode default is true - err = clusterInstance.StartVtworker(cell, "--command_display_interval", "10ms") - require.NoError(t, err) - - // Initial clone (online). - err = clusterInstance.VtworkerProcess.ExecuteCommand("SplitClone", "--", - "--offline=false", - "--chunk_count", "10", - "--min_rows_per_chunk", "1", - "--min_healthy_rdonly_tablets", "1", - "--max_tps", "9999", - shard3Ks) - require.NoError(t, err) - - // Check values in the merge shard - checkValues(t, *shard3.PrimaryTablet(), []string{"INT64(86)", "INT64(1)", `VARCHAR("msg1")`, fmt.Sprintf("UINT64(%d)", key1)}, - 1, true, tableName, fixedParentID, keyspaceName, shardingKeyType, nil) - checkValues(t, *shard3.PrimaryTablet(), []string{"INT64(86)", "INT64(2)", `VARCHAR("msg2")`, fmt.Sprintf("UINT64(%d)", key2)}, - 2, true, tableName, fixedParentID, keyspaceName, shardingKeyType, nil) - - // Reset vtworker such that we can run the next command. - err = clusterInstance.VtworkerProcess.ExecuteCommand("Reset") - require.NoError(t, err) - - // Delete row 2 (provokes an insert). - _, err = shard3Primary.VttabletProcess.QueryTablet("delete from resharding1 where id=2", keyspaceName, true) - require.NoError(t, err) - // Update row 3 (provokes an update). - _, err = shard3Primary.VttabletProcess.QueryTablet("update resharding1 set msg='msg-not-1' where id=1", keyspaceName, true) - require.NoError(t, err) - - // Insert row 4 (provokes a delete). - insertValue(t, shard3.PrimaryTablet(), keyspaceName, tableName, 4, "msg4", key3) - - err = clusterInstance.VtworkerProcess.ExecuteCommand( - "SplitClone", "--", - "--chunk_count", "10", - "--min_rows_per_chunk", "1", - "--min_healthy_rdonly_tablets", "1", - "--max_tps", "9999", - shard3Ks) - require.NoError(t, err) - - // Change tablet, which was taken offline, back to rdonly. - err = clusterInstance.VtctlclientProcess.ExecuteCommand("ChangeTabletType", shard0Rdonly.Alias, "rdonly") - require.NoError(t, err) - err = clusterInstance.VtctlclientProcess.ExecuteCommand("ChangeTabletType", shard1Rdonly.Alias, "rdonly") - require.NoError(t, err) - - // Terminate worker daemon because it is no longer needed. - err = clusterInstance.VtworkerProcess.TearDown() - require.NoError(t, err) - - // Check startup values - checkStartupValues(t, shardingKeyType) - - // check the schema too - err = clusterInstance.VtctlclientProcess.ExecuteCommand("ValidateSchemaKeyspace", keyspaceName) - require.NoError(t, err) - - // Verify vreplication table entries - qr, err := shard3.PrimaryTablet().VttabletProcess.QueryTabletWithDB("select * from vreplication", "_vt") - require.NoError(t, err) - assert.Equal(t, 2, len(qr.Rows)) - assert.Contains(t, fmt.Sprintf("%v", qr.Rows), "SplitClone") - assert.Contains(t, fmt.Sprintf("%v", qr.Rows), `"keyspace:\"ks\" shard:\"-40\" key_range:{end:\"\\x80\"}"`) - assert.Contains(t, fmt.Sprintf("%v", qr.Rows), `"keyspace:\"ks\" shard:\"40-80\" key_range:{end:\"\\x80\"}"`) - - // check the binlog players are running and exporting vars - sharding.CheckDestinationPrimary(t, *shard3Primary, []string{shard1Ks, shard0Ks}, *clusterInstance) - - // When the binlog players/filtered replication is turned on, the query - // service must be turned off on the destination primaries. - // The tested behavior is a safeguard to prevent that somebody can - // accidentally modify data on the destination primaries while they are not - // migrated yet and the source shards are still the source of truth. - err = shard3Primary.VttabletProcess.WaitForTabletStatus("NOT_SERVING") - require.NoError(t, err) - - // check that binlog server exported the stats vars - sharding.CheckBinlogServerVars(t, *shard0Replica, 0, 0, false) - sharding.CheckBinlogServerVars(t, *shard1Replica, 0, 0, false) - - // testing filtered replication: insert a bunch of data on shard 1, check we get most of it after a few seconds, - // wait for binlog server timeout, check we get all of it. - log.Info("Inserting lots of data on source shard") - insertLots(t, 100, 0, tableName, fixedParentID, keyspaceName) - - //Checking 100 percent of data is sent quickly - assert.True(t, checkLotsTimeout(t, 100, 0, tableName, keyspaceName, shardingKeyType)) - - sharding.CheckBinlogPlayerVars(t, *shard3Primary, []string{shard1Ks, shard0Ks}, 30) - - sharding.CheckBinlogServerVars(t, *shard0Replica, 100, 100, false) - sharding.CheckBinlogServerVars(t, *shard1Replica, 100, 100, false) - - // use vtworker to compare the data (after health-checking the destination - // rdonly tablets so discovery works) - err = clusterInstance.VtctlclientProcess.ExecuteCommand("RunHealthCheck", shard3Rdonly.Alias) - require.NoError(t, err) - - // use vtworker to compare the data - clusterInstance.VtworkerProcess.Cell = cell - - // Compare using SplitDiff - log.Info("Running vtworker SplitDiff") - err = clusterInstance.VtworkerProcess.ExecuteVtworkerCommand(clusterInstance.GetAndReservePort(), - clusterInstance.GetAndReservePort(), - "--use_v3_resharding_mode=true", - "SplitDiff", "--", - "--exclude_tables", "unrelated", - "--min_healthy_rdonly_tablets", "1", - "--source_uid", "1", - shard3Ks) - require.NoError(t, err) - - err = clusterInstance.VtctlclientProcess.ExecuteCommand("ChangeTabletType", shard0Rdonly.Alias, "rdonly") - require.NoError(t, err) - err = clusterInstance.VtctlclientProcess.ExecuteCommand("ChangeTabletType", shard3Rdonly.Alias, "rdonly") - require.NoError(t, err) - - log.Info("Running vtworker SplitDiff on second half") - - err = clusterInstance.VtworkerProcess.ExecuteVtworkerCommand(clusterInstance.GetAndReservePort(), - clusterInstance.GetAndReservePort(), - "--use_v3_resharding_mode=true", - "SplitDiff", "--", - "--exclude_tables", "unrelated", - "--min_healthy_rdonly_tablets", "1", - "--source_uid", "2", - shard3Ks) - require.NoError(t, err) - - err = clusterInstance.VtctlclientProcess.ExecuteCommand("ChangeTabletType", shard1Rdonly.Alias, "rdonly") - require.NoError(t, err) - err = clusterInstance.VtctlclientProcess.ExecuteCommand("ChangeTabletType", shard3Rdonly.Alias, "rdonly") - require.NoError(t, err) - - sharding.CheckTabletQueryService(t, *shard3Primary, "NOT_SERVING", false, *clusterInstance) - streamHealth, err := clusterInstance.VtctlclientProcess.ExecuteCommandWithOutput( - "VtTabletStreamHealth", "--", - "--count", "1", shard3Primary.Alias) - require.NoError(t, err) - log.Info("Got health: ", streamHealth) - - var streamHealthResponse querypb.StreamHealthResponse - err = json.Unmarshal([]byte(streamHealth), &streamHealthResponse) - require.NoError(t, err) - assert.Equal(t, streamHealthResponse.Serving, false) - assert.NotNil(t, streamHealthResponse.RealtimeStats) - - // now serve rdonly from the split shards, in cell1 only - err = clusterInstance.VtctlclientProcess.ExecuteCommand( - "MigrateServedTypes", shard3Ks, "rdonly") - require.NoError(t, err) - - // check srv keyspace - expectedPartitions = map[topodatapb.TabletType][]string{} - expectedPartitions[topodatapb.TabletType_PRIMARY] = []string{shard0.Name, shard1.Name, shard2.Name} - expectedPartitions[topodatapb.TabletType_RDONLY] = []string{shard3.Name, shard2.Name} - expectedPartitions[topodatapb.TabletType_REPLICA] = []string{shard0.Name, shard1.Name, shard2.Name} - sharding.CheckSrvKeyspace(t, cell, keyspaceName, expectedPartitions, *clusterInstance) - - sharding.CheckTabletQueryService(t, *shard0Rdonly, "NOT_SERVING", true, *clusterInstance) - sharding.CheckTabletQueryService(t, *shard1Rdonly, "NOT_SERVING", true, *clusterInstance) - - // Now serve replica from the split shards - err = clusterInstance.VtctlclientProcess.ExecuteCommand( - "MigrateServedTypes", shard3Ks, "replica") - require.NoError(t, err) - - expectedPartitions = map[topodatapb.TabletType][]string{} - expectedPartitions[topodatapb.TabletType_PRIMARY] = []string{shard0.Name, shard1.Name, shard2.Name} - expectedPartitions[topodatapb.TabletType_RDONLY] = []string{shard3.Name, shard2.Name} - expectedPartitions[topodatapb.TabletType_REPLICA] = []string{shard3.Name, shard2.Name} - sharding.CheckSrvKeyspace(t, cell, keyspaceName, expectedPartitions, *clusterInstance) - - // now serve from the split shards - err = clusterInstance.VtctlclientProcess.ExecuteCommand( - "MigrateServedTypes", shard3Ks, "primary") - require.NoError(t, err) - - expectedPartitions = map[topodatapb.TabletType][]string{} - expectedPartitions[topodatapb.TabletType_PRIMARY] = []string{shard3.Name, shard2.Name} - expectedPartitions[topodatapb.TabletType_RDONLY] = []string{shard3.Name, shard2.Name} - expectedPartitions[topodatapb.TabletType_REPLICA] = []string{shard3.Name, shard2.Name} - sharding.CheckSrvKeyspace(t, cell, keyspaceName, expectedPartitions, *clusterInstance) - - sharding.CheckTabletQueryService(t, *shard0Primary, "NOT_SERVING", true, *clusterInstance) - sharding.CheckTabletQueryService(t, *shard1Primary, "NOT_SERVING", true, *clusterInstance) - - // check destination shards are serving - sharding.CheckTabletQueryService(t, *shard3Primary, "SERVING", false, *clusterInstance) - - // check the binlog players are gone now - err = shard3Primary.VttabletProcess.WaitForBinLogPlayerCount(0) - require.NoError(t, err) - - // delete the original tablets in the original shard - var wg sync.WaitGroup - for _, shard := range []cluster.Shard{*shard0, *shard1} { - for _, tablet := range shard.Vttablets { - wg.Add(1) - go func(tablet *cluster.Vttablet) { - defer wg.Done() - _ = tablet.VttabletProcess.TearDown() - _ = tablet.MysqlctlProcess.Stop() - }(tablet) - } - } - wg.Wait() - - for _, tablet := range []cluster.Vttablet{*shard0Replica, *shard1Replica, *shard0Rdonly, *shard1Rdonly} { - err = clusterInstance.VtctlclientProcess.ExecuteCommand("DeleteTablet", tablet.Alias) - require.NoError(t, err) - } - - for _, tablet := range []cluster.Vttablet{*shard0Primary, *shard1Primary} { - err = clusterInstance.VtctlclientProcess.ExecuteCommand("DeleteTablet", "--", "--allow_primary", tablet.Alias) - require.NoError(t, err) - } - - // rebuild the serving graph, all mentions of the old shards should be gone - err = clusterInstance.VtctlclientProcess.ExecuteCommand("RebuildKeyspaceGraph", keyspaceName) - require.NoError(t, err) - -} - -func insertStartupValues(t *testing.T) { - insertSQL := fmt.Sprintf(insertTabletTemplateKsID, "resharding1", fixedParentID, 1, "msg1", key1, key1, 1) - sharding.ExecuteOnTablet(t, insertSQL, *shard0.PrimaryTablet(), keyspaceName, false) - - insertSQL = fmt.Sprintf(insertTabletTemplateKsID, "resharding1", fixedParentID, 2, "msg2", key2, key2, 2) - sharding.ExecuteOnTablet(t, insertSQL, *shard1.PrimaryTablet(), keyspaceName, false) - - insertSQL = fmt.Sprintf(insertTabletTemplateKsID, "resharding1", fixedParentID, 3, "msg3", key3, key3, 3) - sharding.ExecuteOnTablet(t, insertSQL, *shard2.PrimaryTablet(), keyspaceName, false) -} - -func insertValue(t *testing.T, tablet *cluster.Vttablet, keyspaceName string, tableName string, id int, msg string, ksID uint64) { - insertSQL := fmt.Sprintf(insertTabletTemplateKsID, tableName, fixedParentID, id, msg, ksID, ksID, id) - sharding.ExecuteOnTablet(t, insertSQL, *tablet, keyspaceName, false) -} - -func checkStartupValues(t *testing.T, shardingKeyType querypb.Type) { - for _, tablet := range shard3.Vttablets { - checkValues(t, *tablet, []string{"INT64(86)", "INT64(1)", `VARCHAR("msg1")`, fmt.Sprintf("UINT64(%d)", key1)}, - 1, true, "resharding1", fixedParentID, keyspaceName, shardingKeyType, nil) - - checkValues(t, *tablet, []string{"INT64(86)", "INT64(2)", `VARCHAR("msg2")`, fmt.Sprintf("UINT64(%d)", key2)}, - 2, true, "resharding1", fixedParentID, keyspaceName, shardingKeyType, nil) - } -} - -// checkLotsTimeout waits till all values are inserted -func checkLotsTimeout(t *testing.T, count uint64, base uint64, table string, keyspaceName string, keyType querypb.Type) bool { - timeout := time.Now().Add(10 * time.Second) - for time.Now().Before(timeout) { - percentFound := checkLots(t, count, base, table, keyspaceName, keyType) - if percentFound == 100 { - return true - } - time.Sleep(300 * time.Millisecond) - } - return false -} - -func checkLots(t *testing.T, count uint64, base uint64, table string, keyspaceName string, keyType querypb.Type) float32 { - shard3Replica := *shard3.Vttablets[1] - - ctx := context.Background() - dbParams := getDBparams(shard3Replica, keyspaceName) - dbConn, _ := mysql.Connect(ctx, &dbParams) - defer dbConn.Close() - - var isFound bool - var totalFound int - var i uint64 - for i = 0; i < count; i++ { - isFound = checkValues(t, shard3Replica, []string{"INT64(86)", - fmt.Sprintf("INT64(%d)", 10000+base+i), - fmt.Sprintf(`VARCHAR("msg-range0-%d")`, 10000+base+i), - fmt.Sprintf("UINT64(%d)", key1)}, - 10000+base+i, true, table, fixedParentID, keyspaceName, keyType, dbConn) - if isFound { - totalFound++ - } - - isFound = checkValues(t, shard3Replica, []string{"INT64(86)", - fmt.Sprintf("INT64(%d)", 20000+base+i), - fmt.Sprintf(`VARCHAR("msg-range1-%d")`, 20000+base+i), - fmt.Sprintf("UINT64(%d)", key2)}, - 20000+base+i, true, table, fixedParentID, keyspaceName, keyType, dbConn) - if isFound { - totalFound++ - } - } - return float32(totalFound * 100 / int(count) / 2) -} - -func checkValues(t *testing.T, vttablet cluster.Vttablet, values []string, id uint64, exists bool, tableName string, - parentID int, ks string, keyType querypb.Type, dbConn *mysql.Conn) bool { - query := fmt.Sprintf("select parent_id, id, msg, custom_ksid_col from %s where parent_id = %d and id = %d", tableName, parentID, id) - var result *sqltypes.Result - var err error - if dbConn != nil { - result, err = dbConn.ExecuteFetch(query, 1000, true) - require.NoError(t, err) - } else { - result, err = vttablet.VttabletProcess.QueryTablet(query, ks, true) - require.NoError(t, err) - } - - isFound := false - if exists && len(result.Rows) > 0 { - isFound = assert.Equal(t, result.Rows[0][0].String(), values[0]) - isFound = isFound && assert.Equal(t, result.Rows[0][1].String(), values[1]) - isFound = isFound && assert.Equal(t, result.Rows[0][2].String(), values[2]) - if keyType == querypb.Type_VARBINARY { - r := strings.NewReplacer("UINT64(", "VARBINARY(\"", ")", "\")") - expected := r.Replace(values[3]) - isFound = isFound && assert.Equal(t, result.Rows[0][3].String(), expected) - } else { - isFound = isFound && assert.Equal(t, result.Rows[0][3].String(), values[3]) - } - - } else { - assert.Equal(t, len(result.Rows), 0) - } - return isFound -} - -// insertLots inserts multiple values to vttablet -func insertLots(t *testing.T, count uint64, base uint64, table string, parentID int, ks string) { - var query1, query2 string - var i uint64 - for i = 0; i < count; i++ { - query1 = fmt.Sprintf(insertTabletTemplateKsID, table, parentID, 10000+base+i, - fmt.Sprintf("msg-range0-%d", 10000+base+i), key1, key1, 10000+base+i) - query2 = fmt.Sprintf(insertTabletTemplateKsID, table, parentID, 20000+base+i, - fmt.Sprintf("msg-range1-%d", 20000+base+i), key2, key2, 20000+base+i) - - sharding.ExecuteOnTablet(t, query1, *shard0.PrimaryTablet(), ks, false) - sharding.ExecuteOnTablet(t, query2, *shard1.PrimaryTablet(), ks, false) - } -} - -func getDBparams(vttablet cluster.Vttablet, ks string) mysql.ConnParams { - dbParams := mysql.ConnParams{ - Uname: "vt_dba", - UnixSocket: path.Join(vttablet.VttabletProcess.Directory, "mysql.sock"), - DbName: "vt_" + ks, - } - return dbParams -} diff --git a/go/test/endtoend/sharding/mergesharding/string/mergesharding_string_test.go b/go/test/endtoend/sharding/mergesharding/string/mergesharding_string_test.go deleted file mode 100644 index 95b1dbf01f7..00000000000 --- a/go/test/endtoend/sharding/mergesharding/string/mergesharding_string_test.go +++ /dev/null @@ -1,29 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v3 - -import ( - "testing" - - sharding "vitess.io/vitess/go/test/endtoend/sharding/mergesharding" -) - -// TestMergeShardingStringShardingKey - tests merge sharding using a String column -func TestMergeShardingStringShardingKey(t *testing.T) { - sharding.TestMergesharding(t, true /* useVarbinaryShardingKeyType */) - -} diff --git a/go/test/endtoend/sharding/resharding/resharding_base.go b/go/test/endtoend/sharding/resharding/resharding_base.go deleted file mode 100644 index 30541a14b7f..00000000000 --- a/go/test/endtoend/sharding/resharding/resharding_base.go +++ /dev/null @@ -1,1315 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package resharding - -import ( - "context" - "encoding/json" - "fmt" - "os/exec" - "path" - "strings" - "sync" - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "vitess.io/vitess/go/mysql" - "vitess.io/vitess/go/sqltypes" - "vitess.io/vitess/go/vt/log" - - "vitess.io/vitess/go/test/endtoend/cluster" - "vitess.io/vitess/go/test/endtoend/sharding" - - querypb "vitess.io/vitess/go/vt/proto/query" - topodatapb "vitess.io/vitess/go/vt/proto/topodata" -) - -var ( - // ClusterInstance instance to be used for test with different params - clusterInstance *cluster.LocalProcessCluster - hostname = "localhost" - keyspaceName = "ks" - cell1 = "zone1" - cell2 = "zone2" - createTabletTemplate = ` - create table %s( - custom_ksid_col %s not null, - msg varchar(64), - id bigint not null, - parent_id bigint not null, - primary key (parent_id, id), - index by_msg (msg) - ) Engine=InnoDB; - ` - createTableBindataTemplate = ` - create table %s( - custom_ksid_col %s not null, - id bigint not null, - parent_id bigint not null, - msg bit(8), - primary key (parent_id, id), - index by_msg (msg) - ) Engine=InnoDB; - ` - createViewTemplate = ` - create view %s (parent_id, id, msg, custom_ksid_col) as select parent_id, id, msg, custom_ksid_col from %s; - ` - createTimestampTable = ` - create table timestamps( - id int not null, - time_milli bigint(20) unsigned not null, - custom_ksid_col %s not null, - primary key (id) - ) Engine=InnoDB; - ` - // Make sure that clone and diff work with tables which have no primary key. - // Work with RBR only - createNoPkTable = ` - create table no_pk( - custom_ksid_col %s not null, - msg varchar(64), - id bigint not null, - parent_id bigint not null - ) Engine=InnoDB; - ` - createUnrelatedTable = ` - create table unrelated( - custom_ksid_col bigint not null, - name varchar(64), - primary key (name) - ) Engine=InnoDB; - ` - fixedParentID = 86 - tableName = "resharding1" - vSchema = ` - { - "sharded": true, - "vindexes": { - "hash_index": { - "type": "hash" - } - }, - "tables": { - "resharding1": { - "column_vindexes": [ - { - "column": "custom_ksid_col", - "name": "hash_index" - } - ] - }, - "resharding2": { - "column_vindexes": [ - { - "column": "custom_ksid_col", - "name": "hash_index" - } - ] - }, - "resharding3": { - "column_vindexes": [ - { - "column": "custom_ksid_col", - "name": "hash_index" - } - ] - }, - "no_pk": { - "column_vindexes": [ - { - "column": "custom_ksid_col", - "name": "hash_index" - } - ] - }, - "timestamps": { - "column_vindexes": [ - { - "column": "custom_ksid_col", - "name": "hash_index" - } - ] - }, - "unrelated": { - "column_vindexes": [ - { - "column": "custom_ksid_col", - "name": "hash_index" - } - ] - } - } - } - ` - - // initial shards - // range '' - 80 & range 80 - '' - shard0 = &cluster.Shard{Name: "-80"} - shard1 = &cluster.Shard{Name: "80-"} - - // split shards - // range 80 - c0 & range c0 - '' - shard2 = &cluster.Shard{Name: "80-c0"} - shard3 = &cluster.Shard{Name: "c0-"} - - // Sharding keys - key1 uint64 = 1152921504606846976 // Key redirect to shard 0 - key2 uint64 = 14987979559889010688 // key redirect to shard 1 (& 2 after Resharding) - key3 uint64 = 10376293541461622784 // Key redirect to shard 1 (& 3 after Resharding) - key4 uint64 = 10376293541461622789 // Key redirect to shard 1 (& 3 after Resharding) - key5 uint64 = 14987979559889010670 // key redirect to shard 1 (& 2 after Resharding) - key6 uint64 = 17293822569102704640 - - // insertTabletTemplateKsID common insert format - insertTabletTemplateKsID = `insert into %s (parent_id, id, msg, custom_ksid_col) values (%d, %d, '%s', %d) /* vtgate:: keyspace_id:%d */ /* id:%d */` -) - -// TestResharding - main test with accepts different params for various test -func TestResharding(t *testing.T, useVarbinaryShardingKeyType bool) { - defer cluster.PanicHandler(t) - clusterInstance = cluster.NewCluster(cell1, hostname) - defer clusterInstance.Teardown() - - // Launch keyspace - keyspace := &cluster.Keyspace{Name: keyspaceName} - - // Start topo server - err := clusterInstance.StartTopo() - require.Nil(t, err) - - // Adding another cell in the same cluster - err = clusterInstance.TopoProcess.ManageTopoDir("mkdir", "/vitess/"+cell2) - require.Nil(t, err) - err = clusterInstance.VtctlProcess.AddCellInfo(cell2) - require.Nil(t, err) - - // Defining all the tablets - shard0Primary := clusterInstance.NewVttabletInstance("replica", 0, "") - shard0Replica := clusterInstance.NewVttabletInstance("replica", 0, "") - shard0RdonlyZ2 := clusterInstance.NewVttabletInstance("rdonly", 0, cell2) - - shard1Primary := clusterInstance.NewVttabletInstance("replica", 0, "") - shard1Replica1 := clusterInstance.NewVttabletInstance("replica", 0, "") - shard1Replica2 := clusterInstance.NewVttabletInstance("replica", 0, "") - shard1Rdonly := clusterInstance.NewVttabletInstance("rdonly", 0, "") - shard1RdonlyZ2 := clusterInstance.NewVttabletInstance("rdonly", 0, cell2) - - shard2Primary := clusterInstance.NewVttabletInstance("replica", 0, "") - shard2Replica1 := clusterInstance.NewVttabletInstance("replica", 0, "") - shard2Replica2 := clusterInstance.NewVttabletInstance("replica", 0, "") - shard2Rdonly := clusterInstance.NewVttabletInstance("rdonly", 0, "") - - shard3Primary := clusterInstance.NewVttabletInstance("replica", 0, "") - shard3Replica := clusterInstance.NewVttabletInstance("replica", 0, "") - shard3Rdonly := clusterInstance.NewVttabletInstance("rdonly", 0, "") - - shard0.Vttablets = []*cluster.Vttablet{shard0Primary, shard0Replica, shard0RdonlyZ2} - shard1.Vttablets = []*cluster.Vttablet{shard1Primary, shard1Replica1, shard1Replica2, shard1Rdonly, shard1RdonlyZ2} - shard2.Vttablets = []*cluster.Vttablet{shard2Primary, shard2Replica1, shard2Replica2, shard2Rdonly} - shard3.Vttablets = []*cluster.Vttablet{shard3Primary, shard3Replica, shard3Rdonly} - - clusterInstance.VtTabletExtraArgs = []string{ - "--vreplication_healthcheck_topology_refresh", "1s", - "--vreplication_healthcheck_retry_delay", "1s", - "--vreplication_retry_delay", "1s", - "--degraded_threshold", "5s", - "--lock_tables_timeout", "5s", - "--watch_replication_stream", - "--enable_semi_sync", - "--enable_replication_reporter", - "--enable-tx-throttler", - "--binlog_use_v3_resharding_mode=true", - } - - shardingColumnType := "bigint(20) unsigned" - shardingKeyType := querypb.Type_UINT64 - - if useVarbinaryShardingKeyType { - shardingColumnType = "varbinary(64)" - shardingKeyType = querypb.Type_VARBINARY - } - - // Initialize Cluster - err = clusterInstance.SetupCluster(keyspace, []cluster.Shard{*shard0, *shard1, *shard2, *shard3}) - require.Nil(t, err) - assert.Equal(t, len(clusterInstance.Keyspaces[0].Shards), 4) - - vtctldClientProcess := cluster.VtctldClientProcessInstance("localhost", clusterInstance.VtctldProcess.GrpcPort, clusterInstance.TmpDirectory) - out, err := vtctldClientProcess.ExecuteCommandWithOutput("SetKeyspaceDurabilityPolicy", keyspaceName, "--durability-policy=semi_sync") - require.NoError(t, err, out) - - //Start MySql - var mysqlCtlProcessList []*exec.Cmd - for _, shard := range clusterInstance.Keyspaces[0].Shards { - for _, tablet := range shard.Vttablets { - log.Infof("Starting mysql for tablet %v", tablet.Alias) - if proc, err := tablet.MysqlctlProcess.StartProcess(); err != nil { - t.Fatal(err) - } else { - mysqlCtlProcessList = append(mysqlCtlProcessList, proc) - } - } - } - - // Wait for mysql processes to start - for _, proc := range mysqlCtlProcessList { - if err := proc.Wait(); err != nil { - t.Fatal(err) - } - } - - // Rebuild keyspace Graph - err = clusterInstance.VtctlclientProcess.ExecuteCommand("RebuildKeyspaceGraph", keyspaceName) - require.Nil(t, err) - - // Start Tablets - for _, shard := range clusterInstance.Keyspaces[0].Shards { - for _, tablet := range shard.Vttablets { - err = tablet.VttabletProcess.Setup() - require.Nil(t, err) - } - } - - // InitShardPrimary - // Init Shard primary - err = clusterInstance.VtctlclientProcess.InitializeShard(keyspaceName, shard0.Name, shard0Primary.Cell, shard0Primary.TabletUID) - require.NoError(t, err) - err = clusterInstance.VtctlclientProcess.InitializeShard(keyspaceName, shard1.Name, shard1Primary.Cell, shard1Primary.TabletUID) - require.NoError(t, err) - - // InitShardPrimary on Split Shards - err = clusterInstance.VtctlclientProcess.InitializeShard(keyspaceName, shard2.Name, shard2Primary.Cell, shard2Primary.TabletUID) - require.NoError(t, err) - err = clusterInstance.VtctlclientProcess.InitializeShard(keyspaceName, shard3.Name, shard3Primary.Cell, shard3Primary.TabletUID) - require.NoError(t, err) - - // Wait for tablets to come in Service state - err = shard0Primary.VttabletProcess.WaitForTabletStatus("SERVING") - require.Nil(t, err) - err = shard1Primary.VttabletProcess.WaitForTabletStatus("SERVING") - require.Nil(t, err) - err = shard2Primary.VttabletProcess.WaitForTabletStatus("SERVING") - require.Nil(t, err) - err = shard3Primary.VttabletProcess.WaitForTabletStatus("SERVING") - require.Nil(t, err) - - // keyspace/shard name fields - shard0Ks := fmt.Sprintf("%s/%s", keyspaceName, shard0.Name) - shard1Ks := fmt.Sprintf("%s/%s", keyspaceName, shard1.Name) - shard2Ks := fmt.Sprintf("%s/%s", keyspaceName, shard2.Name) - shard3Ks := fmt.Sprintf("%s/%s", keyspaceName, shard3.Name) - - // check for shards - result, err := clusterInstance.VtctlclientProcess.ExecuteCommandWithOutput("FindAllShardsInKeyspace", keyspaceName) - require.Nil(t, err) - resultMap := make(map[string]any) - err = json.Unmarshal([]byte(result), &resultMap) - require.Nil(t, err) - assert.Equal(t, 4, len(resultMap), "No of shards should be 4") - - // Apply Schema - err = clusterInstance.VtctlclientProcess.ApplySchema(keyspaceName, fmt.Sprintf(createTabletTemplate, "resharding1", shardingColumnType)) - require.Nil(t, err) - err = clusterInstance.VtctlclientProcess.ApplySchema(keyspaceName, fmt.Sprintf(createTabletTemplate, "resharding2", shardingColumnType)) - require.Nil(t, err) - err = clusterInstance.VtctlclientProcess.ApplySchema(keyspaceName, fmt.Sprintf(createTableBindataTemplate, "resharding3", shardingColumnType)) - require.Nil(t, err) - err = clusterInstance.VtctlclientProcess.ApplySchema(keyspaceName, fmt.Sprintf(createViewTemplate, "view1", "resharding3")) - require.Nil(t, err) - err = clusterInstance.VtctlclientProcess.ApplySchema(keyspaceName, fmt.Sprintf(createNoPkTable, shardingColumnType)) - require.Nil(t, err) - err = clusterInstance.VtctlclientProcess.ApplySchema(keyspaceName, fmt.Sprintf(createTimestampTable, shardingColumnType)) - require.Nil(t, err) - err = clusterInstance.VtctlclientProcess.ApplySchema(keyspaceName, createUnrelatedTable) - require.Nil(t, err) - - // Apply VSchema - err = clusterInstance.VtctlclientProcess.ApplyVSchema(keyspaceName, vSchema) - require.Nil(t, err) - - // Insert Data - insertStartupValues(t) - - // run a health check on source replicas so they respond to discovery - // (for binlog players) and on the source rdonlys (for workers) - for _, shard := range keyspace.Shards { - for _, tablet := range shard.Vttablets { - err = clusterInstance.VtctlclientProcess.ExecuteCommand("RunHealthCheck", tablet.Alias) - require.Nil(t, err) - } - } - - // Rebuild keyspace Graph - err = clusterInstance.VtctlclientProcess.ExecuteCommand("RebuildKeyspaceGraph", keyspaceName) - require.Nil(t, err) - - // check srv keyspace - expectedPartitions := map[topodatapb.TabletType][]string{} - expectedPartitions[topodatapb.TabletType_PRIMARY] = []string{shard0.Name, shard1.Name} - expectedPartitions[topodatapb.TabletType_REPLICA] = []string{shard0.Name, shard1.Name} - expectedPartitions[topodatapb.TabletType_RDONLY] = []string{shard0.Name, shard1.Name} - sharding.CheckSrvKeyspace(t, cell1, keyspaceName, expectedPartitions, *clusterInstance) - - // disable shard1Replica2, so we're sure filtered replication will go from shard1Replica1 - err = clusterInstance.VtctlclientProcess.ExecuteCommand("ChangeTabletType", shard1Replica2.Alias, "spare") - require.Nil(t, err) - - err = shard1Replica2.VttabletProcess.WaitForTabletStatus("NOT_SERVING") - require.Nil(t, err) - - // we need to create the schema, and the worker will do data copying - err = clusterInstance.VtctlclientProcess.ExecuteCommand("CopySchemaShard", "--", "--exclude_tables", "unrelated", - shard1.Rdonly().Alias, fmt.Sprintf("%s/%s", keyspaceName, shard2.Name)) - require.Nil(t, err) - err = clusterInstance.VtctlclientProcess.ExecuteCommand("CopySchemaShard", "--", "--exclude_tables", "unrelated", - shard1.Rdonly().Alias, fmt.Sprintf("%s/%s", keyspaceName, shard3.Name)) - require.Nil(t, err) - - // Run vtworker as daemon for the following SplitClone commands. --use_v3_resharding_mode default is true - err = clusterInstance.StartVtworker(cell1, "--command_display_interval", "10ms") - require.Nil(t, err) - - // Copy the data from the source to the destination shards. - // --max_tps is only specified to enable the throttler and ensure that the - // code is executed. But the intent here is not to throttle the test, hence - // the rate limit is set very high. - - // Initial clone (online). - err = clusterInstance.VtworkerProcess.ExecuteCommand("SplitClone", "--", - "--offline=false", - "--exclude_tables", "unrelated", - "--chunk_count", "10", - "--min_rows_per_chunk", "1", - "--min_healthy_rdonly_tablets", "1", - "--max_tps", "9999", - shard1Ks) - require.Nil(t, err) - - // Check values in the split shard - checkValues(t, *shard2.PrimaryTablet(), []string{"INT64(86)", "INT64(2)", `VARCHAR("msg2")`, fmt.Sprintf("UINT64(%d)", key2)}, - 2, true, tableName, fixedParentID, keyspaceName, shardingKeyType, nil) - checkValues(t, *shard3.PrimaryTablet(), []string{"INT64(86)", "INT64(2)", `VARCHAR("msg2")`, fmt.Sprintf("UINT64(%d)", key2)}, - 2, false, tableName, fixedParentID, keyspaceName, shardingKeyType, nil) - - // Reset vtworker such that we can run the next command. - err = clusterInstance.VtworkerProcess.ExecuteCommand("Reset") - require.Nil(t, err) - - // Test the correct handling of keyspace_id changes which happen after the first clone. - sql := fmt.Sprintf("update resharding1 set custom_ksid_col=%d WHERE id=2", key3) - _, err = shard1Primary.VttabletProcess.QueryTablet(sql, keyspaceName, true) - require.Nil(t, err) - - err = clusterInstance.VtworkerProcess.ExecuteCommand("SplitClone", "--", - "--offline=false", - "--exclude_tables", "unrelated", - "--chunk_count", "10", - "--min_rows_per_chunk", "1", - "--min_healthy_rdonly_tablets", "1", - "--max_tps", "9999", - shard1Ks) - require.Nil(t, err) - - checkValues(t, *shard2.PrimaryTablet(), []string{"INT64(86)", "INT64(2)", `VARCHAR("msg2")`, fmt.Sprintf("UINT64(%d)", key3)}, - 2, false, tableName, fixedParentID, keyspaceName, shardingKeyType, nil) - checkValues(t, *shard3.PrimaryTablet(), []string{"INT64(86)", "INT64(2)", `VARCHAR("msg2")`, fmt.Sprintf("UINT64(%d)", key3)}, - 2, true, tableName, fixedParentID, keyspaceName, shardingKeyType, nil) - - err = clusterInstance.VtworkerProcess.ExecuteCommand("Reset") - require.Nil(t, err) - - // Move row 2 back to shard 2 from shard 3 by changing the keyspace_id again - sql = fmt.Sprintf("update resharding1 set custom_ksid_col=%d WHERE id=2", key2) - _, err = shard1Primary.VttabletProcess.QueryTablet(sql, keyspaceName, true) - require.Nil(t, err) - - err = clusterInstance.VtworkerProcess.ExecuteCommand("SplitClone", "--", - "--offline=false", - "--exclude_tables", "unrelated", - "--chunk_count", "10", - "--min_rows_per_chunk", "1", - "--min_healthy_rdonly_tablets", "1", - "--max_tps", "9999", - shard1Ks) - require.Nil(t, err) - - checkValues(t, *shard2.PrimaryTablet(), []string{"INT64(86)", "INT64(2)", `VARCHAR("msg2")`, fmt.Sprintf("UINT64(%d)", key2)}, - 2, true, tableName, fixedParentID, keyspaceName, shardingKeyType, nil) - checkValues(t, *shard3.PrimaryTablet(), []string{"INT64(86)", "INT64(2)", `VARCHAR("msg2")`, fmt.Sprintf("UINT64(%d)", key2)}, - 2, false, tableName, fixedParentID, keyspaceName, shardingKeyType, nil) - - // Reset vtworker such that we can run the next command. - err = clusterInstance.VtworkerProcess.ExecuteCommand("Reset") - require.Nil(t, err) - - // Modify the destination shard. SplitClone will revert the changes. - - // Delete row 2 (provokes an insert). - _, err = shard2Primary.VttabletProcess.QueryTablet("delete from resharding1 where id=2", keyspaceName, true) - require.Nil(t, err) - // Update row 3 (provokes an update). - _, err = shard3Primary.VttabletProcess.QueryTablet("update resharding1 set msg='msg-not-3' where id=3", keyspaceName, true) - require.Nil(t, err) - - // Insert row 4 and 5 (provokes a delete). - insertValue(t, shard3.PrimaryTablet(), keyspaceName, tableName, 4, "msg4", key3) - insertValue(t, shard3.PrimaryTablet(), keyspaceName, tableName, 5, "msg5", key3) - - err = clusterInstance.VtworkerProcess.ExecuteCommand("SplitClone", "--", - "--exclude_tables", "unrelated", - "--chunk_count", "10", - "--min_rows_per_chunk", "1", - "--min_healthy_rdonly_tablets", "1", - "--max_tps", "9999", - shard1Ks) - require.Nil(t, err) - - // Change tablet, which was taken offline, back to rdonly. - err = clusterInstance.VtctlclientProcess.ExecuteCommand("ChangeTabletType", shard1Rdonly.Alias, "rdonly") - require.Nil(t, err) - - // Terminate worker daemon because it is no longer needed. - err = clusterInstance.VtworkerProcess.TearDown() - require.Nil(t, err) - - // Check startup values - checkStartupValues(t, shardingKeyType) - - // check the schema too - err = clusterInstance.VtctlclientProcess.ExecuteCommand("ValidateSchemaKeyspace", "--", "--exclude_tables=unrelated", keyspaceName) - require.Nil(t, err) - - // Verify vreplication table entries - qr, err := shard2Primary.VttabletProcess.QueryTabletWithDB("select * from vreplication", "_vt") - require.Nil(t, err) - assert.Equal(t, 1, len(qr.Rows)) - assert.Contains(t, fmt.Sprintf("%v", qr.Rows), "SplitClone") - assert.Contains(t, fmt.Sprintf("%v", qr.Rows), `"keyspace:\"ks\" shard:\"80-\" key_range:{start:\"\\x80\" end:\"\\xc0\"}"`) - - qr, err = shard3.PrimaryTablet().VttabletProcess.QueryTabletWithDB("select * from vreplication", "_vt") - require.Nil(t, err) - assert.Equal(t, 1, len(qr.Rows)) - assert.Contains(t, fmt.Sprintf("%v", qr.Rows), "SplitClone") - assert.Contains(t, fmt.Sprintf("%v", qr.Rows), `"keyspace:\"ks\" shard:\"80-\" key_range:{start:\"\\xc0\"}"`) - - // check the binlog players are running and exporting vars - sharding.CheckDestinationPrimary(t, *shard2Primary, []string{shard1Ks}, *clusterInstance) - sharding.CheckDestinationPrimary(t, *shard3Primary, []string{shard1Ks}, *clusterInstance) - - // When the binlog players/filtered replication is turned on, the query - // service must be turned off on the destination primaries. - // The tested behavior is a safeguard to prevent that somebody can - // accidentally modify data on the destination primaries while they are not - // migrated yet and the source shards are still the source of truth. - err = shard2Primary.VttabletProcess.WaitForTabletStatus("NOT_SERVING") - require.Nil(t, err) - err = shard3Primary.VttabletProcess.WaitForTabletStatus("NOT_SERVING") - require.Nil(t, err) - - // check that binlog server exported the stats vars - sharding.CheckBinlogServerVars(t, *shard1Replica1, 0, 0, false) - - // Check that the throttler was enabled. - // The stream id is hard-coded as 1, which is the first id generated through auto-inc. - sharding.CheckThrottlerService(t, fmt.Sprintf("%s:%d", hostname, shard2Primary.GrpcPort), - []string{"BinlogPlayer/1"}, 9999, *clusterInstance) - sharding.CheckThrottlerService(t, fmt.Sprintf("%s:%d", hostname, shard3Primary.GrpcPort), - []string{"BinlogPlayer/1"}, 9999, *clusterInstance) - - // testing filtered replication: insert a bunch of data on shard 1, check we get most of it after a few seconds, - // wait for binlog server timeout, check we get all of it. - log.Info("Inserting lots of data on source shard") - insertLots(100, 0, *shard1Primary, tableName, fixedParentID, keyspaceName) - log.Info("Executing MultiValue Insert Queries") - execMultiShardDmls(t, keyspaceName) - - // Checking 100 percent of data is sent quickly - assert.True(t, checkLotsTimeout(t, 100, 0, tableName, keyspaceName, shardingKeyType)) - // Checking no data was sent the wrong way - checkLotsNotPresent(t, 100, 0, tableName, keyspaceName, shardingKeyType) - - // Checking MultiValue Insert Queries - checkMultiShardValues(t, keyspaceName, shardingKeyType) - sharding.CheckBinlogPlayerVars(t, *shard2Primary, []string{shard1Ks}, 30) - sharding.CheckBinlogPlayerVars(t, *shard3Primary, []string{shard1Ks}, 30) - - sharding.CheckBinlogServerVars(t, *shard1Replica1, 100, 100, false) - - // use vtworker to compare the data (after health-checking the destination - // rdonly tablets so discovery works) - err = clusterInstance.VtctlclientProcess.ExecuteCommand("RunHealthCheck", shard3Rdonly.Alias) - require.Nil(t, err) - - // use vtworker to compare the data - clusterInstance.VtworkerProcess.Cell = cell1 - - // Compare using SplitDiff - log.Info("Running vtworker SplitDiff") - err = clusterInstance.VtworkerProcess.ExecuteVtworkerCommand(clusterInstance.GetAndReservePort(), - clusterInstance.GetAndReservePort(), - "--use_v3_resharding_mode=true", - "SplitDiff", "--", - "--exclude_tables", "unrelated", - "--min_healthy_rdonly_tablets", "1", - shard3Ks) - require.Nil(t, err) - - // Compare using MultiSplitDiff - log.Info("Running vtworker MultiSplitDiff") - err = clusterInstance.VtworkerProcess.ExecuteVtworkerCommand(clusterInstance.GetAndReservePort(), - clusterInstance.GetAndReservePort(), - "--use_v3_resharding_mode=true", - "MultiSplitDiff", "--", - "--exclude_tables", "unrelated", - shard1Ks) - require.Nil(t, err) - - err = clusterInstance.VtctlclientProcess.ExecuteCommand("ChangeTabletType", shard1Rdonly.Alias, "rdonly") - require.Nil(t, err) - err = clusterInstance.VtctlclientProcess.ExecuteCommand("ChangeTabletType", shard3Rdonly.Alias, "rdonly") - require.Nil(t, err) - - // tests a failover switching serving to a different replica - err = clusterInstance.VtctlclientProcess.ExecuteCommand("ChangeTabletType", shard1Replica2.Alias, "replica") - require.Nil(t, err) - err = clusterInstance.VtctlclientProcess.ExecuteCommand("ChangeTabletType", shard1Replica1.Alias, "spare") - require.Nil(t, err) - - err = shard1Replica2.VttabletProcess.WaitForTabletStatus("SERVING") - require.Nil(t, err) - err = shard1Replica1.VttabletProcess.WaitForTabletStatus("NOT_SERVING") - require.Nil(t, err) - - err = clusterInstance.VtctlclientProcess.ExecuteCommand("RunHealthCheck", shard1Replica2.Alias) - require.Nil(t, err) - - // test data goes through again - log.Info("Inserting lots of data on source shard") - insertLots(100, 100, *shard1Primary, tableName, fixedParentID, keyspaceName) - log.Info("Checking 100 percent of data was sent quickly") - assert.True(t, checkLotsTimeout(t, 100, 100, tableName, keyspaceName, shardingKeyType)) - - sharding.CheckBinlogServerVars(t, *shard1Replica2, 80, 80, false) - - // check we can't migrate the primary just yet - err = clusterInstance.VtctlclientProcess.ExecuteCommand("MigrateServedTypes", shard1Ks, "primary") - require.Error(t, err, "MigrateServedTypes should fail") - - // check query service is off on primary 2 and primary 3, as filtered replication is enabled. - // Even health check that is enabled on primary 3 should not interfere (we run it to be sure). - err = clusterInstance.VtctlclientProcess.ExecuteCommand("RunHealthCheck", shard3Primary.Alias) - require.Nil(t, err) - - for _, primary := range []cluster.Vttablet{*shard2Primary, *shard3Primary} { - sharding.CheckTabletQueryService(t, primary, "NOT_SERVING", false, *clusterInstance) - streamHealth, err := clusterInstance.VtctlclientProcess.ExecuteCommandWithOutput( - "VtTabletStreamHealth", "--", - "--count", "1", primary.Alias) - require.Nil(t, err) - log.Info("Got health: ", streamHealth) - - var streamHealthResponse querypb.StreamHealthResponse - err = json.Unmarshal([]byte(streamHealth), &streamHealthResponse) - require.Nil(t, err) - assert.Equal(t, streamHealthResponse.Serving, false) - assert.NotNil(t, streamHealthResponse.RealtimeStats) - - } - - // now serve rdonly from the split shards, in cell1 only - err = clusterInstance.VtctlclientProcess.ExecuteCommand( - "MigrateServedTypes", "--", fmt.Sprintf("--cells=%s", cell1), - shard1Ks, "rdonly") - require.Nil(t, err) - - // check srv keyspace - expectedPartitions = map[topodatapb.TabletType][]string{} - expectedPartitions[topodatapb.TabletType_PRIMARY] = []string{shard0.Name, shard1.Name} - expectedPartitions[topodatapb.TabletType_RDONLY] = []string{shard0.Name, shard2.Name, shard3.Name} - expectedPartitions[topodatapb.TabletType_REPLICA] = []string{shard0.Name, shard1.Name} - sharding.CheckSrvKeyspace(t, cell1, keyspaceName, expectedPartitions, *clusterInstance) - - // Cell 2 is not affected - expectedPartitions = map[topodatapb.TabletType][]string{} - expectedPartitions[topodatapb.TabletType_PRIMARY] = []string{shard0.Name, shard1.Name} - expectedPartitions[topodatapb.TabletType_RDONLY] = []string{shard0.Name, shard1.Name} - expectedPartitions[topodatapb.TabletType_REPLICA] = []string{shard0.Name, shard1.Name} - sharding.CheckSrvKeyspace(t, cell2, keyspaceName, expectedPartitions, *clusterInstance) - - sharding.CheckTabletQueryService(t, *shard0RdonlyZ2, "SERVING", false, *clusterInstance) - sharding.CheckTabletQueryService(t, *shard1RdonlyZ2, "SERVING", false, *clusterInstance) - sharding.CheckTabletQueryService(t, *shard1Rdonly, "NOT_SERVING", true, *clusterInstance) - - // Shouldn't be able to rebuild keyspace graph while migration is on going - // (i.e there are records that have tablet controls set) - err = clusterInstance.VtctlclientProcess.ExecuteCommand("RebuildKeyspaceGraph", keyspaceName) - require.Error(t, err, "Error expected") - - // rerun migrate to ensure it doesn't fail - // skip refresh to make it go faster - err = clusterInstance.VtctlclientProcess.ExecuteCommand( - "MigrateServedTypes", "--", - fmt.Sprintf("--cells=%s", cell1), - "--skip-refresh-state=true", - shard1Ks, "rdonly") - require.Nil(t, err) - - // now serve rdonly from the split shards, everywhere - err = clusterInstance.VtctlclientProcess.ExecuteCommand( - "MigrateServedTypes", - shard1Ks, "rdonly") - require.Nil(t, err) - - expectedPartitions = map[topodatapb.TabletType][]string{} - expectedPartitions[topodatapb.TabletType_PRIMARY] = []string{shard0.Name, shard1.Name} - expectedPartitions[topodatapb.TabletType_RDONLY] = []string{shard0.Name, shard2.Name, shard3.Name} - expectedPartitions[topodatapb.TabletType_REPLICA] = []string{shard0.Name, shard1.Name} - sharding.CheckSrvKeyspace(t, cell1, keyspaceName, expectedPartitions, *clusterInstance) - // Cell 2 is also changed - sharding.CheckSrvKeyspace(t, cell2, keyspaceName, expectedPartitions, *clusterInstance) - - sharding.CheckTabletQueryService(t, *shard0RdonlyZ2, "SERVING", false, *clusterInstance) - sharding.CheckTabletQueryService(t, *shard1RdonlyZ2, "NOT_SERVING", true, *clusterInstance) - sharding.CheckTabletQueryService(t, *shard1Rdonly, "NOT_SERVING", true, *clusterInstance) - - // rerun migrate to ensure it doesn't fail - // skip refresh to make it go faster - err = clusterInstance.VtctlclientProcess.ExecuteCommand( - "MigrateServedTypes", "--", - "--skip-refresh-state=true", - shard1Ks, "rdonly") - require.Nil(t, err) - - // then serve replica from the split shards - destinationShards := []cluster.Shard{*shard2, *shard3} - err = clusterInstance.VtctlclientProcess.ExecuteCommand( - "MigrateServedTypes", shard1Ks, "replica") - require.Nil(t, err) - - expectedPartitions = map[topodatapb.TabletType][]string{} - expectedPartitions[topodatapb.TabletType_PRIMARY] = []string{shard0.Name, shard1.Name} - expectedPartitions[topodatapb.TabletType_RDONLY] = []string{shard0.Name, shard2.Name, shard3.Name} - expectedPartitions[topodatapb.TabletType_REPLICA] = []string{shard0.Name, shard2.Name, shard3.Name} - sharding.CheckSrvKeyspace(t, cell1, keyspaceName, expectedPartitions, *clusterInstance) - - // move replica back and forth - err = clusterInstance.VtctlclientProcess.ExecuteCommand( - "MigrateServedTypes", "--", "--reverse", - shard1Ks, "replica") - require.Nil(t, err) - - // After a backwards migration, queryservice should be enabled on - // source and disabled on destinations - sharding.CheckTabletQueryService(t, *shard1Replica2, "SERVING", false, *clusterInstance) - - // Destination tablets would have query service disabled for other reasons than the migration, - // so check the shard record instead of the tablets directly. - sharding.CheckShardQueryServices(t, *clusterInstance, destinationShards, cell1, keyspaceName, - topodatapb.TabletType_REPLICA, false) - sharding.CheckShardQueryServices(t, *clusterInstance, destinationShards, cell2, keyspaceName, - topodatapb.TabletType_REPLICA, false) - - expectedPartitions = map[topodatapb.TabletType][]string{} - expectedPartitions[topodatapb.TabletType_PRIMARY] = []string{shard0.Name, shard1.Name} - expectedPartitions[topodatapb.TabletType_RDONLY] = []string{shard0.Name, shard2.Name, shard3.Name} - expectedPartitions[topodatapb.TabletType_REPLICA] = []string{shard0.Name, shard1.Name} - sharding.CheckSrvKeyspace(t, cell1, keyspaceName, expectedPartitions, *clusterInstance) - - err = clusterInstance.VtctlclientProcess.ExecuteCommand( - "MigrateServedTypes", shard1Ks, "replica") - require.Nil(t, err) - // After a forwards migration, queryservice should be disabled on - // source and enabled on destinations - sharding.CheckTabletQueryService(t, *shard1Replica2, "NOT_SERVING", true, *clusterInstance) - // Destination tablets would have query service disabled for other reasons than the migration, - // so check the shard record instead of the tablets directly - sharding.CheckShardQueryServices(t, *clusterInstance, destinationShards, cell1, keyspaceName, - topodatapb.TabletType_REPLICA, true) - sharding.CheckShardQueryServices(t, *clusterInstance, destinationShards, cell2, keyspaceName, - topodatapb.TabletType_REPLICA, true) - - expectedPartitions = map[topodatapb.TabletType][]string{} - expectedPartitions[topodatapb.TabletType_PRIMARY] = []string{shard0.Name, shard1.Name} - expectedPartitions[topodatapb.TabletType_RDONLY] = []string{shard0.Name, shard2.Name, shard3.Name} - expectedPartitions[topodatapb.TabletType_REPLICA] = []string{shard0.Name, shard2.Name, shard3.Name} - sharding.CheckSrvKeyspace(t, cell1, keyspaceName, expectedPartitions, *clusterInstance) - - // reparent shard2 to shard2Replica1, then insert more data and see it flow through still - err = clusterInstance.VtctlclientProcess.ExecuteCommand("PlannedReparentShard", "--", "--keyspace_shard", shard2Ks, - "--new_primary", shard2Replica1.Alias) - require.Nil(t, err) - - // update our test variables to point at the new primary - tmp := shard2Primary - shard2Primary = shard2Replica1 - shard2Replica1 = tmp - - // Insert another set of the data and see it flow through - insertLots(100, 200, *shard1.PrimaryTablet(), tableName, fixedParentID, keyspaceName) - // Checking 100 percent of data is sent fairly quickly - assert.True(t, checkLotsTimeout(t, 100, 200, tableName, keyspaceName, shardingKeyType)) - - // Compare using SplitDiff - log.Info("Running vtworker SplitDiff") - err = clusterInstance.VtworkerProcess.ExecuteVtworkerCommand(clusterInstance.GetAndReservePort(), - clusterInstance.GetAndReservePort(), - "--use_v3_resharding_mode=true", - "SplitDiff", "--", - "--exclude_tables", "unrelated", - "--min_healthy_rdonly_tablets", "1", - shard3Ks) - require.Nil(t, err) - - // Compare using MultiSplitDiff - log.Info("Running vtworker MultiSplitDiff") - err = clusterInstance.VtworkerProcess.ExecuteVtworkerCommand(clusterInstance.GetAndReservePort(), - clusterInstance.GetAndReservePort(), - "--use_v3_resharding_mode=true", - "MultiSplitDiff", "--", - "--exclude_tables", "unrelated", - shard1Ks) - require.Nil(t, err) - - err = clusterInstance.VtctlclientProcess.ExecuteCommand("ChangeTabletType", shard1Rdonly.Alias, "rdonly") - require.Nil(t, err) - err = clusterInstance.VtctlclientProcess.ExecuteCommand("ChangeTabletType", shard3Rdonly.Alias, "rdonly") - require.Nil(t, err) - - // going to migrate the primary now - - // mock with the SourceShard records to test 'vtctl SourceShardDelete' and 'vtctl SourceShardAdd' - err = clusterInstance.VtctlclientProcess.ExecuteCommand("SourceShardDelete", shard3Ks, "1") - require.Nil(t, err) - err = clusterInstance.VtctlclientProcess.ExecuteCommand("SourceShardAdd", "--", "--key_range=80-", - shard3Ks, "1", shard1Ks) - require.Nil(t, err) - - // CancelResharding should fail because migration has started. - err = clusterInstance.VtctlclientProcess.ExecuteCommand("CancelResharding", shard1Ks, "1") - require.Error(t, err) - - // do a Migrate that will fail waiting for replication - // which should cause the Migrate to be canceled and the source - // primary to be serving again. - // This is the legacy resharding migration command - err = clusterInstance.VtctlclientProcess.ExecuteCommand("MigrateServedTypes", "--", - "--filtered_replication_wait_time", "0s", shard1Ks, "primary") - require.Error(t, err) - - expectedPartitions = map[topodatapb.TabletType][]string{} - expectedPartitions[topodatapb.TabletType_PRIMARY] = []string{shard0.Name, shard1.Name} - expectedPartitions[topodatapb.TabletType_RDONLY] = []string{shard0.Name, shard2.Name, shard3.Name} - expectedPartitions[topodatapb.TabletType_REPLICA] = []string{shard0.Name, shard2.Name, shard3.Name} - sharding.CheckSrvKeyspace(t, cell1, keyspaceName, expectedPartitions, *clusterInstance) - - sharding.CheckTabletQueryService(t, *shard1Primary, "SERVING", false, *clusterInstance) - - // sabotage primary migration and make it fail in an unfinished state. - err = clusterInstance.VtctlclientProcess.ExecuteCommand("SetShardTabletControl", "--", - "--denied_tables=t", - shard3Ks, "primary") - require.Nil(t, err) - - err = clusterInstance.VtctlclientProcess.ExecuteCommand("MigrateServedTypes", - shard1Ks, "primary") - require.Error(t, err) - - // Query service is disabled in source shard as failure occurred after point of no return - sharding.CheckTabletQueryService(t, *shard1Primary, "NOT_SERVING", true, *clusterInstance) - - // Global topology records should not change as migration did not succeed - shardInfo := sharding.GetShardInfo(t, shard1Ks, *clusterInstance) - assert.True(t, shardInfo.GetIsPrimaryServing(), "source shards should be set in destination shard") - - shardInfo = sharding.GetShardInfo(t, shard3Ks, *clusterInstance) - assert.Equal(t, 1, len(shardInfo.GetSourceShards()), "source shards should be set in destination shard") - assert.False(t, shardInfo.GetIsPrimaryServing(), "source shards should be set in destination shard") - - shardInfo = sharding.GetShardInfo(t, shard2Ks, *clusterInstance) - assert.Equal(t, 1, len(shardInfo.GetSourceShards()), "source shards should be set in destination shard") - assert.False(t, shardInfo.GetIsPrimaryServing(), "source shards should be set in destination shard") - - // remove sabotage, but make it fail early. This should not result in the source primary serving, - // because this failure is past the point of no return. - err = clusterInstance.VtctlclientProcess.ExecuteCommand("SetShardTabletControl", "--", "--denied_tables=t", - "--remove", shard3Ks, "primary") - require.Nil(t, err) - err = clusterInstance.VtctlclientProcess.ExecuteCommand("MigrateServedTypes", "--", - "--filtered_replication_wait_time", "0s", shard1Ks, "primary") - require.Error(t, err) - - sharding.CheckTabletQueryService(t, *shard1Primary, "NOT_SERVING", true, *clusterInstance) - - // do the migration that's expected to succeed - err = clusterInstance.VtctlclientProcess.ExecuteCommand("MigrateServedTypes", - shard1Ks, "primary") - require.Nil(t, err) - expectedPartitions = map[topodatapb.TabletType][]string{} - expectedPartitions[topodatapb.TabletType_PRIMARY] = []string{shard0.Name, shard2.Name, shard3.Name} - expectedPartitions[topodatapb.TabletType_RDONLY] = []string{shard0.Name, shard2.Name, shard3.Name} - expectedPartitions[topodatapb.TabletType_REPLICA] = []string{shard0.Name, shard2.Name, shard3.Name} - sharding.CheckSrvKeyspace(t, cell1, keyspaceName, expectedPartitions, *clusterInstance) - - sharding.CheckTabletQueryService(t, *shard1Primary, "NOT_SERVING", true, *clusterInstance) - - // check destination shards are serving - sharding.CheckTabletQueryService(t, *shard2Primary, "SERVING", false, *clusterInstance) - sharding.CheckTabletQueryService(t, *shard3Primary, "SERVING", false, *clusterInstance) - - // check the binlog players are gone now - err = shard2Primary.VttabletProcess.WaitForBinLogPlayerCount(0) - require.Nil(t, err) - err = shard3Primary.VttabletProcess.WaitForBinLogPlayerCount(0) - require.Nil(t, err) - - // test reverse_replication - // start with inserting a row in each destination shard - insertValue(t, shard2Primary, keyspaceName, "resharding2", 2, "msg2", key2) - insertValue(t, shard3Primary, keyspaceName, "resharding2", 3, "msg3", key3) - - // ensure the rows are not present yet - checkValues(t, *shard1Primary, []string{"INT64(86)", "INT64(2)", `VARCHAR("msg2")`, fmt.Sprintf("UINT64(%d)", key2)}, - 2, false, "resharding2", fixedParentID, keyspaceName, shardingKeyType, nil) - checkValues(t, *shard1Primary, []string{"INT64(86)", "INT64(3)", `VARCHAR("msg3")`, fmt.Sprintf("UINT64(%d)", key3)}, - 3, false, "resharding2", fixedParentID, keyspaceName, shardingKeyType, nil) - - // repeat the migration with reverse_replication - err = clusterInstance.VtctlclientProcess.ExecuteCommand("MigrateServedTypes", "--", "--reverse_replication=true", - shard1Ks, "primary") - require.Nil(t, err) - // look for the rows in the original primary after a short wait - time.Sleep(1 * time.Second) - checkValues(t, *shard1Primary, []string{"INT64(86)", "INT64(2)", `VARCHAR("msg2")`, fmt.Sprintf("UINT64(%d)", key2)}, - 2, true, "resharding2", fixedParentID, keyspaceName, shardingKeyType, nil) - checkValues(t, *shard1Primary, []string{"INT64(86)", "INT64(3)", `VARCHAR("msg3")`, fmt.Sprintf("UINT64(%d)", key3)}, - 3, true, "resharding2", fixedParentID, keyspaceName, shardingKeyType, nil) - - // retry the migration to ensure it now fails - err = clusterInstance.VtctlclientProcess.ExecuteCommand( - "MigrateServedTypes", "--", - "--reverse_replication=true", - shard1Ks, "primary") - require.Error(t, err) - - // CancelResharding should now succeed - err = clusterInstance.VtctlclientProcess.ExecuteCommand("CancelResharding", shard1Ks) - require.Nil(t, err) - err = shard1Primary.VttabletProcess.WaitForBinLogPlayerCount(0) - require.Nil(t, err) - - // delete the original tablets in the original shard - var wg sync.WaitGroup - for _, tablet := range []*cluster.Vttablet{shard1Primary, shard1Replica1, shard1Replica2, shard1Rdonly, shard1RdonlyZ2} { - wg.Add(1) - go func(tablet *cluster.Vttablet) { - defer wg.Done() - _ = tablet.VttabletProcess.TearDown() - _ = tablet.MysqlctlProcess.Stop() - }(tablet) - } - wg.Wait() - - for _, tablet := range []cluster.Vttablet{*shard1Replica1, *shard1Replica2, *shard1Rdonly, *shard1RdonlyZ2} { - err = clusterInstance.VtctlclientProcess.ExecuteCommand("DeleteTablet", tablet.Alias) - require.Nil(t, err) - } - err = clusterInstance.VtctlclientProcess.ExecuteCommand("DeleteTablet", "--", "--allow_primary", shard1Primary.Alias) - require.Nil(t, err) - - // rebuild the serving graph, all mentions of the old shards should be gone - err = clusterInstance.VtctlclientProcess.ExecuteCommand("RebuildKeyspaceGraph", keyspaceName) - require.Nil(t, err) - - // test RemoveShardCell - err = clusterInstance.VtctlclientProcess.ExecuteCommand("RemoveShardCell", shard0Ks, cell1) - require.Error(t, err) - err = clusterInstance.VtctlclientProcess.ExecuteCommand("RemoveShardCell", shard1Ks, cell1) - require.Nil(t, err) - err = clusterInstance.VtctlclientProcess.ExecuteCommand("RemoveShardCell", shard1Ks, cell2) - require.Nil(t, err) - - shardInfo = sharding.GetShardInfo(t, shard1Ks, *clusterInstance) - assert.Empty(t, shardInfo.GetTabletControls(), "cells not in shard ") - - // delete the original shard - err = clusterInstance.VtctlclientProcess.ExecuteCommand("DeleteShard", shard1Ks) - require.Nil(t, err) - - // make sure we can't delete the destination shard now that it's serving - err = clusterInstance.VtctlclientProcess.ExecuteCommand("DeleteShard", shard2Ks) - require.Error(t, err) - -} - -func insertStartupValues(t *testing.T) { - insertSQL := fmt.Sprintf(insertTabletTemplateKsID, "resharding1", fixedParentID, 1, "msg1", key1, key1, 1) - sharding.ExecuteOnTablet(t, insertSQL, *shard0.PrimaryTablet(), keyspaceName, false) - - insertSQL = fmt.Sprintf(insertTabletTemplateKsID, "resharding1", fixedParentID, 2, "msg2", key2, key2, 2) - sharding.ExecuteOnTablet(t, insertSQL, *shard1.PrimaryTablet(), keyspaceName, false) - - insertSQL = fmt.Sprintf(insertTabletTemplateKsID, "resharding1", fixedParentID, 3, "msg3", key3, key3, 3) - sharding.ExecuteOnTablet(t, insertSQL, *shard1.PrimaryTablet(), keyspaceName, false) - - insertSQL = fmt.Sprintf(insertTabletTemplateKsID, "resharding3", fixedParentID, 1, "a", key1, key1, 1) - sharding.ExecuteOnTablet(t, insertSQL, *shard0.PrimaryTablet(), keyspaceName, false) - - insertSQL = fmt.Sprintf(insertTabletTemplateKsID, "resharding3", fixedParentID, 2, "b", key2, key2, 2) - sharding.ExecuteOnTablet(t, insertSQL, *shard1.PrimaryTablet(), keyspaceName, false) - - insertSQL = fmt.Sprintf(insertTabletTemplateKsID, "resharding3", fixedParentID, 3, "c", key3, key3, 3) - sharding.ExecuteOnTablet(t, insertSQL, *shard1.PrimaryTablet(), keyspaceName, false) - - insertSQL = fmt.Sprintf(insertTabletTemplateKsID, "no_pk", fixedParentID, 1, "msg1", key5, key5, 1) - sharding.ExecuteOnTablet(t, insertSQL, *shard1.PrimaryTablet(), keyspaceName, false) -} - -func insertValue(t *testing.T, tablet *cluster.Vttablet, keyspaceName string, tableName string, id int, msg string, ksID uint64) { - insertSQL := fmt.Sprintf(insertTabletTemplateKsID, tableName, fixedParentID, id, msg, ksID, ksID, id) - sharding.ExecuteOnTablet(t, insertSQL, *tablet, keyspaceName, false) -} - -func execMultiShardDmls(t *testing.T, keyspaceName string) { - ids := []int{10000001, 10000002, 10000003} - msgs := []string{"msg-id10000001", "msg-id10000002", "msg-id10000003"} - ksIds := []uint64{key2, key3, key4} - sharding.InsertMultiValues(t, *shard1.PrimaryTablet(), keyspaceName, "resharding1", fixedParentID, ids, msgs, ksIds) - - ids = []int{10000004, 10000005} - msgs = []string{"msg-id10000004", "msg-id10000005"} - ksIds = []uint64{key3, key4} - sharding.InsertMultiValues(t, *shard1.PrimaryTablet(), keyspaceName, "resharding1", fixedParentID, ids, msgs, ksIds) - - ids = []int{10000011, 10000012, 10000013} - msgs = []string{"msg-id10000011", "msg-id10000012", "msg-id10000013"} - ksIds = []uint64{key2, key3, key4} - sharding.InsertMultiValues(t, *shard1.PrimaryTablet(), keyspaceName, "resharding1", fixedParentID, ids, msgs, ksIds) - - // This update targets two shards. - sql := `update resharding1 set msg="update1" where parent_id=86 and id in (10000011,10000012)` - _, _ = shard1.PrimaryTablet().VttabletProcess.QueryTablet(sql, keyspaceName, true) - - // This update targets one shard. - sql = `update resharding1 set msg="update1" where parent_id=86 and id in (10000013)` - _, _ = shard1.PrimaryTablet().VttabletProcess.QueryTablet(sql, keyspaceName, true) - - ids = []int{10000014, 10000015, 10000016} - msgs = []string{"msg-id10000014", "msg-id10000015", "msg-id10000016"} - ksIds = []uint64{key2, key3, key4} - sharding.InsertMultiValues(t, *shard1.PrimaryTablet(), keyspaceName, "resharding1", fixedParentID, ids, msgs, ksIds) - - // This delete targets two shards. - sql = `delete from resharding1 where parent_id =86 and id in (10000014, 10000015)` - _, _ = shard1.PrimaryTablet().VttabletProcess.QueryTablet(sql, keyspaceName, true) - // This delete targets one shard. - sql = `delete from resharding1 where parent_id =86 and id in (10000016)` - _, _ = shard1.PrimaryTablet().VttabletProcess.QueryTablet(sql, keyspaceName, true) - - // repeat DMLs for table with msg as bit(8) - ids = []int{10000001, 10000002, 10000003} - msgs = []string{"a", "b", "c"} - ksIds = []uint64{key2, key3, key4} - sharding.InsertMultiValues(t, *shard1.PrimaryTablet(), keyspaceName, "resharding3", fixedParentID, ids, msgs, ksIds) - - ids = []int{10000004, 10000005} - msgs = []string{"d", "e"} - ksIds = []uint64{key3, key4} - sharding.InsertMultiValues(t, *shard1.PrimaryTablet(), keyspaceName, "resharding3", fixedParentID, ids, msgs, ksIds) - - ids = []int{10000011, 10000012, 10000013} - msgs = []string{"k", "l", "m"} - ksIds = []uint64{key2, key3, key4} - sharding.InsertMultiValues(t, *shard1.PrimaryTablet(), keyspaceName, "resharding3", fixedParentID, ids, msgs, ksIds) - - // This update targets two shards. - sql = `update resharding3 set msg="g" where parent_id=86 and id in (10000011, 10000012)` - _, _ = shard1.PrimaryTablet().VttabletProcess.QueryTablet(sql, keyspaceName, true) - - // This update targets one shard. - sql = `update resharding3 set msg="h" where parent_id=86 and id in (10000013)` - _, _ = shard1.PrimaryTablet().VttabletProcess.QueryTablet(sql, keyspaceName, true) - - ids = []int{10000014, 10000015, 10000016} - msgs = []string{"n", "o", "p"} - ksIds = []uint64{key2, key3, key4} - sharding.InsertMultiValues(t, *shard1.PrimaryTablet(), keyspaceName, "resharding3", fixedParentID, ids, msgs, ksIds) - - // This delete targets two shards. - sql = `delete from resharding3 where parent_id =86 and id in (10000014, 10000015)` - _, _ = shard1.PrimaryTablet().VttabletProcess.QueryTablet(sql, keyspaceName, true) - // This delete targets one shard. - sql = `delete from resharding3 where parent_id =86 and id in (10000016)` - _, _ = shard1.PrimaryTablet().VttabletProcess.QueryTablet(sql, keyspaceName, true) - -} - -func checkStartupValues(t *testing.T, shardingKeyType querypb.Type) { - // check first value is in the right shard - for _, tablet := range shard2.Vttablets { - checkValues(t, *tablet, []string{"INT64(86)", "INT64(2)", `VARCHAR("msg2")`, fmt.Sprintf("UINT64(%d)", key2)}, - 2, true, "resharding1", fixedParentID, keyspaceName, shardingKeyType, nil) - checkValues(t, *tablet, []string{"INT64(86)", "INT64(2)", `BIT("b")`, fmt.Sprintf("UINT64(%d)", key2)}, - 2, true, "resharding3", fixedParentID, keyspaceName, shardingKeyType, nil) - } - for _, tablet := range shard3.Vttablets { - checkValues(t, *tablet, []string{"INT64(86)", "INT64(2)", `VARCHAR("msg2")`, fmt.Sprintf("UINT64(%d)", key2)}, - 2, false, "resharding1", fixedParentID, keyspaceName, shardingKeyType, nil) - checkValues(t, *tablet, []string{"INT64(86)", "INT64(2)", `BIT("b")`, fmt.Sprintf("UINT64(%d)", key2)}, - 2, false, "resharding3", fixedParentID, keyspaceName, shardingKeyType, nil) - } - // check first value is in the right shard - for _, tablet := range shard2.Vttablets { - checkValues(t, *tablet, []string{"INT64(86)", "INT64(3)", `VARCHAR("msg3")`, fmt.Sprintf("UINT64(%d)", key3)}, - 3, false, "resharding1", fixedParentID, keyspaceName, shardingKeyType, nil) - checkValues(t, *tablet, []string{"INT64(86)", "INT64(3)", `BIT("c")`, fmt.Sprintf("UINT64(%d)", key3)}, - 3, false, "resharding3", fixedParentID, keyspaceName, shardingKeyType, nil) - } - for _, tablet := range shard3.Vttablets { - checkValues(t, *tablet, []string{"INT64(86)", "INT64(3)", `VARCHAR("msg3")`, fmt.Sprintf("UINT64(%d)", key3)}, - 3, true, "resharding1", fixedParentID, keyspaceName, shardingKeyType, nil) - checkValues(t, *tablet, []string{"INT64(86)", "INT64(3)", `BIT("c")`, fmt.Sprintf("UINT64(%d)", key3)}, - 3, true, "resharding3", fixedParentID, keyspaceName, shardingKeyType, nil) - } - - // Check for no_pk table - for _, tablet := range shard2.Vttablets { - checkValues(t, *tablet, []string{"INT64(86)", "INT64(1)", `VARCHAR("msg1")`, fmt.Sprintf("UINT64(%d)", key5)}, - 1, true, "no_pk", fixedParentID, keyspaceName, shardingKeyType, nil) - } - for _, tablet := range shard3.Vttablets { - checkValues(t, *tablet, []string{"INT64(86)", "INT64(1)", `BIT("msg1")`, fmt.Sprintf("UINT64(%d)", key5)}, - 1, false, "no_pk", fixedParentID, keyspaceName, shardingKeyType, nil) - } -} - -// checkLotsNotPresent verifies that no rows should be present in vttablet -func checkLotsNotPresent(t *testing.T, count uint64, base uint64, table string, keyspaceName string, keyType querypb.Type) { - shard2Replica2 := *shard2.Vttablets[2] - shard3Replica1 := *shard3.Vttablets[1] - - ctx := context.Background() - dbParams := getDBparams(shard2Replica2, keyspaceName) - dbConn1, _ := mysql.Connect(ctx, &dbParams) - defer dbConn1.Close() - - dbParams = getDBparams(shard3Replica1, keyspaceName) - dbConn2, _ := mysql.Connect(ctx, &dbParams) - defer dbConn2.Close() - - var i uint64 - //var count uint64 = 1000 - for i = 0; i < count; i++ { - assert.False(t, checkValues(t, shard3Replica1, []string{"INT64(86)", - fmt.Sprintf("INT64(%d)", 10000+base+i), - fmt.Sprintf(`VARCHAR("msg-range1-%d")`, 10000+base+i), - fmt.Sprintf("UINT64(%d)", key5)}, - 10000+base+i, false, table, fixedParentID, keyspaceName, keyType, dbConn2)) - - assert.False(t, checkValues(t, shard2Replica2, []string{"INT64(86)", - fmt.Sprintf("INT64(%d)", 20000+base+i), - fmt.Sprintf(`VARCHAR("msg-range2-%d")`, 20000+base+i), - fmt.Sprintf("UINT64(%d)", key4)}, - 20000+base+i, false, table, fixedParentID, keyspaceName, keyType, dbConn1)) - } -} - -// checkLotsTimeout waits till all values are inserted -func checkLotsTimeout(t *testing.T, count uint64, base uint64, table string, keyspaceName string, keyType querypb.Type) bool { - timeout := time.Now().Add(10 * time.Second) - for time.Now().Before(timeout) { - percentFound := checkLots(t, count, base, table, keyspaceName, keyType) - if percentFound == 100 { - return true - } - time.Sleep(300 * time.Millisecond) - } - return false -} - -func checkLots(t *testing.T, count uint64, base uint64, table string, keyspaceName string, keyType querypb.Type) float32 { - shard2Replica2 := *shard2.Vttablets[2] - shard3Replica1 := *shard3.Vttablets[1] - - ctx := context.Background() - dbParams := getDBparams(shard2Replica2, keyspaceName) - dbConn1, _ := mysql.Connect(ctx, &dbParams) - defer dbConn1.Close() - - dbParams = getDBparams(shard3Replica1, keyspaceName) - dbConn2, _ := mysql.Connect(ctx, &dbParams) - defer dbConn2.Close() - - var isFound bool - var totalFound int - var i uint64 - for i = 0; i < count; i++ { - isFound = checkValues(t, shard2Replica2, []string{"INT64(86)", - fmt.Sprintf("INT64(%d)", 10000+base+i), - fmt.Sprintf(`VARCHAR("msg-range1-%d")`, 10000+base+i), - fmt.Sprintf("UINT64(%d)", key5)}, - 10000+base+i, true, table, fixedParentID, keyspaceName, keyType, dbConn1) - if isFound { - totalFound++ - } - - isFound = checkValues(t, shard3Replica1, []string{"INT64(86)", - fmt.Sprintf("INT64(%d)", 20000+base+i), - fmt.Sprintf(`VARCHAR("msg-range2-%d")`, 20000+base+i), - fmt.Sprintf("UINT64(%d)", key4)}, - 20000+base+i, true, table, fixedParentID, keyspaceName, keyType, dbConn2) - if isFound { - totalFound++ - } - } - return float32(totalFound * 100 / int(count) / 2) -} - -func checkValues(t *testing.T, vttablet cluster.Vttablet, values []string, id uint64, exists bool, tableName string, - parentID int, ks string, keyType querypb.Type, dbConn *mysql.Conn) bool { - query := fmt.Sprintf("select parent_id, id, msg, custom_ksid_col from %s where parent_id = %d and id = %d", tableName, parentID, id) - var result *sqltypes.Result - var err error - if dbConn != nil { - result, err = dbConn.ExecuteFetch(query, 1000, true) - require.Nil(t, err) - } else { - result, err = vttablet.VttabletProcess.QueryTablet(query, ks, true) - require.Nil(t, err) - } - - isFound := false - if exists && len(result.Rows) > 0 { - isFound = assert.Equal(t, result.Rows[0][0].String(), values[0]) - isFound = isFound && assert.Equal(t, result.Rows[0][1].String(), values[1]) - isFound = isFound && assert.Equal(t, result.Rows[0][2].String(), values[2]) - if keyType == querypb.Type_VARBINARY { - r := strings.NewReplacer("UINT64(", "VARBINARY(\"", ")", "\")") - expected := r.Replace(values[3]) - isFound = isFound && assert.Equal(t, result.Rows[0][3].String(), expected) - } else { - isFound = isFound && assert.Equal(t, result.Rows[0][3].String(), values[3]) - } - - } else { - assert.Equal(t, len(result.Rows), 0) - } - return isFound -} - -func checkMultiShardValues(t *testing.T, keyspaceName string, keyType querypb.Type) { - //Shard2 primary, replica1 and replica 2 - shard2Tablets := []cluster.Vttablet{*shard2.Vttablets[0], *shard2.Vttablets[1], *shard2.Vttablets[2]} - checkMultiDbs(t, shard2Tablets, "resharding1", keyspaceName, keyType, 10000001, "msg-id10000001", key2, true) - checkMultiDbs(t, shard2Tablets, "resharding1", keyspaceName, keyType, 10000002, "msg-id10000002", key3, false) - checkMultiDbs(t, shard2Tablets, "resharding1", keyspaceName, keyType, 10000003, "msg-id10000003", key4, false) - checkMultiDbs(t, shard2Tablets, "resharding1", keyspaceName, keyType, 10000004, "msg-id10000004", key3, false) - checkMultiDbs(t, shard2Tablets, "resharding1", keyspaceName, keyType, 10000005, "msg-id10000005", key4, false) - - //Shard 3 primary and replica - shard3Tablets := []cluster.Vttablet{*shard3.Vttablets[0], *shard3.Vttablets[1]} - checkMultiDbs(t, shard3Tablets, "resharding1", keyspaceName, keyType, 10000001, "msg-id10000001", key2, false) - checkMultiDbs(t, shard3Tablets, "resharding1", keyspaceName, keyType, 10000002, "msg-id10000002", key3, true) - checkMultiDbs(t, shard3Tablets, "resharding1", keyspaceName, keyType, 10000003, "msg-id10000003", key4, true) - checkMultiDbs(t, shard3Tablets, "resharding1", keyspaceName, keyType, 10000004, "msg-id10000004", key3, true) - checkMultiDbs(t, shard3Tablets, "resharding1", keyspaceName, keyType, 10000005, "msg-id10000005", key4, true) - - // Updated values - checkMultiDbs(t, shard2Tablets, "resharding1", keyspaceName, keyType, 10000011, "update1", key2, true) - checkMultiDbs(t, shard3Tablets, "resharding1", keyspaceName, keyType, 10000012, "update1", key3, true) - checkMultiDbs(t, shard3Tablets, "resharding1", keyspaceName, keyType, 10000013, "update2", key4, true) - - allTablets := []cluster.Vttablet{*shard2.Vttablets[0], *shard2.Vttablets[1], *shard2.Vttablets[2], *shard3.Vttablets[0], *shard3.Vttablets[1]} - checkMultiDbs(t, allTablets, "resharding1", keyspaceName, keyType, 10000014, "msg-id10000014", key2, false) - checkMultiDbs(t, allTablets, "resharding1", keyspaceName, keyType, 10000015, "msg-id10000015", key3, false) - checkMultiDbs(t, allTablets, "resharding1", keyspaceName, keyType, 10000016, "msg-id10000016", key6, false) - - // checks for bit(8) table - checkMultiDbs(t, shard2Tablets, "resharding3", keyspaceName, keyType, 10000001, "a", key2, true) - checkMultiDbs(t, shard2Tablets, "resharding3", keyspaceName, keyType, 10000002, "b", key3, false) - checkMultiDbs(t, shard2Tablets, "resharding3", keyspaceName, keyType, 10000003, "c", key4, false) - checkMultiDbs(t, shard2Tablets, "resharding3", keyspaceName, keyType, 10000004, "d", key3, false) - checkMultiDbs(t, shard2Tablets, "resharding3", keyspaceName, keyType, 10000005, "e", key4, false) - - checkMultiDbs(t, shard3Tablets, "resharding3", keyspaceName, keyType, 10000001, "a", key2, false) - checkMultiDbs(t, shard3Tablets, "resharding3", keyspaceName, keyType, 10000002, "b", key3, true) - checkMultiDbs(t, shard3Tablets, "resharding3", keyspaceName, keyType, 10000003, "c", key4, true) - checkMultiDbs(t, shard3Tablets, "resharding3", keyspaceName, keyType, 10000004, "d", key3, true) - checkMultiDbs(t, shard3Tablets, "resharding3", keyspaceName, keyType, 10000005, "e", key4, true) - - // updated values - checkMultiDbs(t, shard2Tablets, "resharding3", keyspaceName, keyType, 10000011, "g", key2, true) - checkMultiDbs(t, shard3Tablets, "resharding3", keyspaceName, keyType, 10000012, "g", key3, true) - checkMultiDbs(t, shard3Tablets, "resharding3", keyspaceName, keyType, 10000013, "h", key4, true) - - checkMultiDbs(t, allTablets, "resharding3", keyspaceName, keyType, 10000014, "n", key2, false) - checkMultiDbs(t, allTablets, "resharding3", keyspaceName, keyType, 10000015, "o", key3, false) - checkMultiDbs(t, allTablets, "resharding3", keyspaceName, keyType, 10000016, "p", key6, false) - -} - -// checkMultiDbs checks the row in multiple dbs -func checkMultiDbs(t *testing.T, vttablets []cluster.Vttablet, tableName string, keyspaceName string, - keyType querypb.Type, id int, msg string, ksID uint64, presentInDb bool) { - - for _, tablet := range vttablets { - checkValues(t, tablet, []string{"INT64(86)", - fmt.Sprintf("INT64(%d)", id), - fmt.Sprintf(`VARCHAR("%s")`, msg), - fmt.Sprintf("UINT64(%d)", ksID)}, - ksID, presentInDb, tableName, fixedParentID, keyspaceName, keyType, nil) - } -} - -// insertLots inserts multiple values to vttablet -func insertLots(count uint64, base uint64, vttablet cluster.Vttablet, table string, parentID int, ks string) { - ctx := context.Background() - dbParams := getDBparams(vttablet, ks) - dbConn, _ := mysql.Connect(ctx, &dbParams) - defer dbConn.Close() - - var query1, query2 string - var i uint64 - for i = 0; i < count; i++ { - query1 = fmt.Sprintf(insertTabletTemplateKsID, table, parentID, 10000+base+i, - fmt.Sprintf("msg-range1-%d", 10000+base+i), key5, key5, 10000+base+i) - query2 = fmt.Sprintf(insertTabletTemplateKsID, table, parentID, 20000+base+i, - fmt.Sprintf("msg-range2-%d", 20000+base+i), key4, key4, 20000+base+i) - - insertToTabletUsingSameConn(query1, dbConn) - insertToTabletUsingSameConn(query2, dbConn) - } -} - -// insertToTabletUsingSameConn inserts a single row to vttablet using existing connection -func insertToTabletUsingSameConn(query string, dbConn *mysql.Conn) { - _, err := dbConn.ExecuteFetch(query, 1000, true) - if err != nil { - log.Errorf("error inserting data into tablet, query: %v, error: %v", query, err) - } -} - -func getDBparams(vttablet cluster.Vttablet, ks string) mysql.ConnParams { - dbParams := mysql.ConnParams{ - Uname: "vt_dba", - UnixSocket: path.Join(vttablet.VttabletProcess.Directory, "mysql.sock"), - DbName: "vt_" + ks, - } - return dbParams -} diff --git a/go/test/endtoend/sharding/resharding/string/resharding_string_test.go b/go/test/endtoend/sharding/resharding/string/resharding_string_test.go deleted file mode 100644 index ccff0481b77..00000000000 --- a/go/test/endtoend/sharding/resharding/string/resharding_string_test.go +++ /dev/null @@ -1,29 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v3 - -import ( - "testing" - - sharding "vitess.io/vitess/go/test/endtoend/sharding/resharding" -) - -// TestReshardingString - using a VARBINARY column for resharding. -func TestReshardingString(t *testing.T) { - sharding.TestResharding(t, true) - -} diff --git a/go/test/endtoend/sharding/resharding/v3/resharding_v3_test.go b/go/test/endtoend/sharding/resharding/v3/resharding_v3_test.go deleted file mode 100644 index 6805b1c759a..00000000000 --- a/go/test/endtoend/sharding/resharding/v3/resharding_v3_test.go +++ /dev/null @@ -1,29 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v3 - -import ( - "testing" - - sharding "vitess.io/vitess/go/test/endtoend/sharding/resharding" -) - -// TestV3ReSharding - main tests resharding using a INT column -func TestV3ReSharding(t *testing.T) { - sharding.TestResharding(t, false) - -} diff --git a/go/test/endtoend/sharding/verticalsplit/vertical_split_test.go b/go/test/endtoend/sharding/verticalsplit/vertical_split_test.go deleted file mode 100644 index c83b3d48e09..00000000000 --- a/go/test/endtoend/sharding/verticalsplit/vertical_split_test.go +++ /dev/null @@ -1,724 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package verticalsplit - -import ( - "context" - "flag" - "fmt" - "os/exec" - "path" - "reflect" - "strings" - "testing" - "time" - - "vitess.io/vitess/go/vt/vtgate/planbuilder" - - "vitess.io/vitess/go/vt/vtgate/evalengine" - - "vitess.io/vitess/go/json2" - "vitess.io/vitess/go/vt/vtgate/vtgateconn" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "vitess.io/vitess/go/mysql" - "vitess.io/vitess/go/sqltypes" - "vitess.io/vitess/go/test/endtoend/cluster" - "vitess.io/vitess/go/test/endtoend/sharding" - "vitess.io/vitess/go/vt/log" - "vitess.io/vitess/go/vt/proto/topodata" - _ "vitess.io/vitess/go/vt/vtgate/grpcvtgateconn" -) - -var ( - clusterInstance *cluster.LocalProcessCluster - sourceKeyspace = "source_keyspace" - destinationKeyspace = "destination_keyspace" - hostname = "localhost" - cellj = "test_nj" - shardName = "0" - createTabletTemplate = ` - create table %s( - id bigint not null, - msg varchar(64), - primary key (id), - index by_msg (msg) - ) Engine=InnoDB;` - createViewTemplate = "create view %s(id, msg) as select id, msg from %s;" - createMoving3NoPkTable = ` - create table moving3_no_pk ( - id bigint not null, - msg varchar(64) - ) Engine=InnoDB;` - tableArr = []string{"moving1", "moving2", "staying1", "staying2"} - insertIndex = 0 - moving1First int - moving2First int - staying1First int - staying2First int - moving3NoPkFirst int -) - -func TestVerticalSplit(t *testing.T) { - defer cluster.PanicHandler(t) - flag.Parse() - code, err := initializeCluster() - if err != nil { - t.Errorf("setup failed with status code %d", code) - } - defer teardownCluster() - - // Adding another cell in the same cluster - err = clusterInstance.TopoProcess.ManageTopoDir("mkdir", "/vitess/"+"test_ca") - require.NoError(t, err) - err = clusterInstance.VtctlProcess.AddCellInfo("test_ca") - require.NoError(t, err) - err = clusterInstance.TopoProcess.ManageTopoDir("mkdir", "/vitess/"+"test_ny") - require.NoError(t, err) - err = clusterInstance.VtctlProcess.AddCellInfo("test_ny") - require.NoError(t, err) - - require.NoError(t, err, "error should be Nil") - - // source keyspace, with 4 tables - sourceShard := clusterInstance.Keyspaces[0].Shards[0] - sourcePrimaryTablet := *sourceShard.Vttablets[0] - sourceReplicaTablet := *sourceShard.Vttablets[1] - sourceRdOnlyTablet1 := *sourceShard.Vttablets[2] - sourceRdOnlyTablet2 := *sourceShard.Vttablets[3] - sourceKs := fmt.Sprintf("%s/%s", sourceKeyspace, shardName) - - // destination keyspace, with just two tables - destinationShard := clusterInstance.Keyspaces[1].Shards[0] - destinationPrimaryTablet := *destinationShard.Vttablets[0] - destinationReplicaTablet := *destinationShard.Vttablets[1] - destinationRdOnlyTablet1 := *destinationShard.Vttablets[2] - destinationRdOnlyTablet2 := *destinationShard.Vttablets[3] - - // source and destination primary and replica tablets will be started - for _, tablet := range []cluster.Vttablet{sourcePrimaryTablet, sourceReplicaTablet, destinationPrimaryTablet, destinationReplicaTablet} { - _ = tablet.VttabletProcess.Setup() - } - - // rdonly tablets will be started - for _, tablet := range []cluster.Vttablet{sourceRdOnlyTablet1, sourceRdOnlyTablet2, destinationRdOnlyTablet1, destinationRdOnlyTablet2} { - _ = tablet.VttabletProcess.Setup() - } - - // RebuildKeyspaceGraph source keyspace - err = clusterInstance.VtctlclientProcess.ExecuteCommand("RebuildKeyspaceGraph", sourceKeyspace) - require.NoError(t, err) - - // RebuildKeyspaceGraph destination keyspace - err = clusterInstance.VtctlclientProcess.ExecuteCommand("RebuildKeyspaceGraph", destinationKeyspace) - require.NoError(t, err) - - // check SrvKeyspace - ksServedFrom := "ServedFrom(primary): source_keyspace\nServedFrom(rdonly): source_keyspace\nServedFrom(replica): source_keyspace\n" - checkSrvKeyspaceServedFrom(t, cellj, destinationKeyspace, ksServedFrom, *clusterInstance) - - // reparent to make the tablets work (we use health check, fix their types) - err = clusterInstance.VtctlclientProcess.InitializeShard(sourceKeyspace, shardName, cellj, sourcePrimaryTablet.TabletUID) - require.NoError(t, err) - err = clusterInstance.VtctlclientProcess.InitializeShard(destinationKeyspace, shardName, cellj, destinationPrimaryTablet.TabletUID) - require.NoError(t, err) - - sourcePrimaryTablet.Type = "primary" - destinationPrimaryTablet.Type = "primary" - - for _, tablet := range []cluster.Vttablet{sourceReplicaTablet, destinationReplicaTablet} { - _ = tablet.VttabletProcess.WaitForTabletStatus("SERVING") - require.NoError(t, err) - } - for _, tablet := range []cluster.Vttablet{sourceRdOnlyTablet1, sourceRdOnlyTablet2, destinationRdOnlyTablet1, destinationRdOnlyTablet2} { - _ = tablet.VttabletProcess.WaitForTabletStatus("SERVING") - require.NoError(t, err) - } - - for _, tablet := range []cluster.Vttablet{sourcePrimaryTablet, destinationPrimaryTablet, sourceReplicaTablet, destinationReplicaTablet, sourceRdOnlyTablet1, sourceRdOnlyTablet2, destinationRdOnlyTablet1, destinationRdOnlyTablet2} { - assert.Equal(t, tablet.VttabletProcess.GetTabletStatus(), "SERVING") - } - - // Apply schema on source. - for _, tableName := range tableArr { - _, err := sourcePrimaryTablet.VttabletProcess.QueryTablet(fmt.Sprintf(createTabletTemplate, tableName), sourceKeyspace, true) - require.NoError(t, err) - } - _, err = sourcePrimaryTablet.VttabletProcess.QueryTablet(fmt.Sprintf(createViewTemplate, "view1", "moving1"), sourceKeyspace, true) - require.NoError(t, err) - _, err = sourcePrimaryTablet.VttabletProcess.QueryTablet(createMoving3NoPkTable, sourceKeyspace, true) - require.NoError(t, err) - err = clusterInstance.VtctlclientProcess.ExecuteCommand("ReloadSchemaShard", "source_keyspace/0") - require.NoError(t, err) - - // Alloy schema on destination. - _, err = destinationPrimaryTablet.VttabletProcess.QueryTablet(fmt.Sprintf(createTabletTemplate, "extra1"), destinationKeyspace, true) - require.NoError(t, err) - err = clusterInstance.VtctlclientProcess.ExecuteCommand("ReloadSchemaShard", "destination_keyspace/0") - require.NoError(t, err) - - err = clusterInstance.StartVtgate() - require.NoError(t, err) - - vtParams := mysql.ConnParams{ - Host: clusterInstance.Hostname, - Port: clusterInstance.VtgateMySQLPort, - } - - ctx := context.Background() - conn, err := mysql.Connect(ctx, &vtParams) - require.NoError(t, err) - defer conn.Close() - - err = clusterInstance.VtgateProcess.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.primary", sourceKeyspace, shardName), 1) - require.NoError(t, err) - err = clusterInstance.VtgateProcess.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.replica", sourceKeyspace, shardName), 1) - require.NoError(t, err) - err = clusterInstance.VtgateProcess.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.rdonly", sourceKeyspace, shardName), 2) - require.NoError(t, err) - err = clusterInstance.VtgateProcess.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.primary", destinationKeyspace, shardName), 1) - require.NoError(t, err) - err = clusterInstance.VtgateProcess.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.replica", destinationKeyspace, shardName), 1) - require.NoError(t, err) - err = clusterInstance.VtgateProcess.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.rdonly", destinationKeyspace, shardName), 2) - require.NoError(t, err) - - // create the schema on the source keyspace, add some values - insertInitialValues(t, conn, sourcePrimaryTablet, destinationPrimaryTablet) - - err = clusterInstance.VtctlclientProcess.ExecuteCommand("CopySchemaShard", "--", "--tables", "/moving/,view1", sourceRdOnlyTablet1.Alias, "destination_keyspace/0") - require.NoError(t, err, "CopySchemaShard failed") - - // starting vtworker - httpPort := clusterInstance.GetAndReservePort() - grpcPort := clusterInstance.GetAndReservePort() - clusterInstance.VtworkerProcess = *cluster.VtworkerProcessInstance( - httpPort, - grpcPort, - clusterInstance.TopoPort, - clusterInstance.Hostname, - clusterInstance.TmpDirectory) - - err = clusterInstance.VtworkerProcess.ExecuteVtworkerCommand(httpPort, grpcPort, - "--cell", cellj, - "--command_display_interval", "10ms", - "--use_v3_resharding_mode=true", - "VerticalSplitClone", "--", - "--tables", "/moving/,view1", - "--chunk_count", "10", - "--min_rows_per_chunk", "1", - "--min_healthy_tablets", "1", "destination_keyspace/0") - require.NoError(t, err) - - // test Cancel first - err = clusterInstance.VtctlclientProcess.ExecuteCommand("CancelResharding", "destination_keyspace/0") - require.NoError(t, err) - err = destinationPrimaryTablet.VttabletProcess.WaitForBinLogPlayerCount(0) - require.NoError(t, err) - // primary should be in serving state after cancel - sharding.CheckTabletQueryServices(t, []cluster.Vttablet{destinationPrimaryTablet}, "SERVING", false, *clusterInstance) - - // redo VerticalSplitClone - err = clusterInstance.VtworkerProcess.ExecuteVtworkerCommand(clusterInstance.GetAndReservePort(), clusterInstance.GetAndReservePort(), - "--cell", cellj, - "--command_display_interval", "10ms", - "--use_v3_resharding_mode=true", - "VerticalSplitClone", "--", - "--tables", "/moving/,view1", - "--chunk_count", "10", - "--min_rows_per_chunk", "1", - "--min_healthy_tablets", "1", "destination_keyspace/0") - require.NoError(t, err) - - // check values are present - checkValues(t, &destinationPrimaryTablet, destinationKeyspace, "vt_destination_keyspace", "moving1", moving1First, 100) - checkValues(t, &destinationPrimaryTablet, destinationKeyspace, "vt_destination_keyspace", "moving2", moving2First, 100) - checkValues(t, &destinationPrimaryTablet, destinationKeyspace, "vt_destination_keyspace", "view1", moving1First, 100) - checkValues(t, &destinationPrimaryTablet, destinationKeyspace, "vt_destination_keyspace", "moving3_no_pk", moving3NoPkFirst, 100) - - // Verify vreplication table entries - dbParams := mysql.ConnParams{ - Uname: "vt_dba", - UnixSocket: path.Join(destinationPrimaryTablet.VttabletProcess.Directory, "mysql.sock"), - } - dbParams.DbName = "_vt" - dbConn, err := mysql.Connect(ctx, &dbParams) - require.NoError(t, err) - qr, err := dbConn.ExecuteFetch("select * from vreplication", 1000, true) - require.NoError(t, err, "error should be Nil") - assert.Equal(t, 1, len(qr.Rows)) - assert.Contains(t, fmt.Sprintf("%v", qr.Rows), "SplitClone") - assert.Contains(t, fmt.Sprintf("%v", qr.Rows), `keyspace:\"source_keyspace\" shard:\"0\" tables:\"/moving/\" tables:\"view1\`) - dbConn.Close() - - // check the binlog player is running and exporting vars - sharding.CheckDestinationPrimary(t, destinationPrimaryTablet, []string{sourceKs}, *clusterInstance) - - // check that binlog server exported the stats vars - sharding.CheckBinlogServerVars(t, sourceReplicaTablet, 0, 0, true) - - // add values to source, make sure they're replicated - moving1FirstAdd1 := insertValues(t, conn, sourceKeyspace, "moving1", 100) - _ = insertValues(t, conn, sourceKeyspace, "staying1", 100) - moving2FirstAdd1 := insertValues(t, conn, sourceKeyspace, "moving2", 100) - checkValuesTimeout(t, destinationPrimaryTablet, "moving1", moving1FirstAdd1, 100, 30) - checkValuesTimeout(t, destinationPrimaryTablet, "moving2", moving2FirstAdd1, 100, 30) - sharding.CheckBinlogPlayerVars(t, destinationPrimaryTablet, []string{sourceKs}, 30) - sharding.CheckBinlogServerVars(t, sourceReplicaTablet, 100, 100, true) - - // use vtworker to compare the data - log.Info("Running vtworker VerticalSplitDiff") - err = clusterInstance.VtworkerProcess.ExecuteVtworkerCommand(clusterInstance.GetAndReservePort(), - clusterInstance.GetAndReservePort(), - "--use_v3_resharding_mode=true", - "--cell", "test_nj", - "VerticalSplitDiff", "--", - "--min_healthy_rdonly_tablets", "1", - "destination_keyspace/0") - require.NoError(t, err) - - // check query service is off on destination primary, as filtered - // replication is enabled. Even health check should not interfere. - destinationPrimaryTabletVars := destinationPrimaryTablet.VttabletProcess.GetVars() - assert.NotNil(t, destinationPrimaryTabletVars) - assert.Contains(t, reflect.ValueOf(destinationPrimaryTabletVars["TabletStateName"]).String(), "NOT_SERVING") - - // check we can't migrate the primary just yet - err = clusterInstance.VtctlclientProcess.ExecuteCommand("MigrateServedFrom", "destination_keyspace/0", "primary") - require.Error(t, err) - - // migrate rdonly only in test_ny cell, make sure nothing is migrated - // in test_nj - err = clusterInstance.VtctlclientProcess.ExecuteCommand("MigrateServedFrom", "--cells=test_ny", "destination_keyspace/0", "rdonly") - require.NoError(t, err) - - // check SrvKeyspace - checkSrvKeyspaceServedFrom(t, cellj, destinationKeyspace, ksServedFrom, *clusterInstance) - checkDeniedTables(t, sourcePrimaryTablet, sourceKeyspace, nil) - checkDeniedTables(t, sourceReplicaTablet, sourceKeyspace, nil) - checkDeniedTables(t, sourceRdOnlyTablet1, sourceKeyspace, nil) - checkDeniedTables(t, sourceRdOnlyTablet2, sourceKeyspace, nil) - - // migrate test_nj only, using command line manual fix command, - // and restore it back. - keyspaceJSON, err := clusterInstance.VtctlclientProcess.ExecuteCommandWithOutput("GetKeyspace", "destination_keyspace") - require.NoError(t, err) - - validateKeyspaceJSON(t, keyspaceJSON, []string{"test_ca", "test_nj"}) - - err = clusterInstance.VtctlclientProcess.ExecuteCommand("SetKeyspaceServedFrom", "--", "--source=source_keyspace", "--remove", "--cells=test_nj,test_ca", "destination_keyspace", "rdonly") - require.NoError(t, err) - - // again validating keyspaceJSON - keyspaceJSON, err = clusterInstance.VtctlclientProcess.ExecuteCommandWithOutput("GetKeyspace", "destination_keyspace") - require.NoError(t, err) - - validateKeyspaceJSON(t, keyspaceJSON, nil) - - err = clusterInstance.VtctlclientProcess.ExecuteCommand("SetKeyspaceServedFrom", "--", "--source=source_keyspace", "destination_keyspace", "rdonly") - require.NoError(t, err) - - keyspaceJSON, err = clusterInstance.VtctlclientProcess.ExecuteCommandWithOutput("GetKeyspace", "destination_keyspace") - require.NoError(t, err) - - validateKeyspaceJSON(t, keyspaceJSON, []string{}) - - // now serve rdonly from the destination shards - err = clusterInstance.VtctlclientProcess.ExecuteCommand("MigrateServedFrom", "destination_keyspace/0", "rdonly") - require.NoError(t, err) - checkSrvKeyspaceServedFrom(t, cellj, destinationKeyspace, "ServedFrom(primary): source_keyspace\nServedFrom(replica): source_keyspace\n", *clusterInstance) - checkDeniedTables(t, sourcePrimaryTablet, sourceKeyspace, nil) - checkDeniedTables(t, sourceReplicaTablet, sourceKeyspace, nil) - checkDeniedTables(t, sourceRdOnlyTablet1, sourceKeyspace, []string{"/moving/", "view1"}) - checkDeniedTables(t, sourceRdOnlyTablet2, sourceKeyspace, []string{"/moving/", "view1"}) - - grpcAddress := fmt.Sprintf("%s:%d", "localhost", clusterInstance.VtgateProcess.GrpcPort) - gconn, err := vtgateconn.Dial(ctx, grpcAddress) - require.NoError(t, err) - defer gconn.Close() - - checkClientConnRedirectionExecuteKeyrange(ctx, t, gconn, destinationKeyspace, []topodata.TabletType{topodata.TabletType_PRIMARY, topodata.TabletType_REPLICA}, []string{"moving1", "moving2"}) - - // then serve replica from the destination shards - err = clusterInstance.VtctlclientProcess.ExecuteCommand("MigrateServedFrom", "destination_keyspace/0", "replica") - require.NoError(t, err) - checkSrvKeyspaceServedFrom(t, cellj, destinationKeyspace, "ServedFrom(primary): source_keyspace\n", *clusterInstance) - checkDeniedTables(t, sourcePrimaryTablet, sourceKeyspace, nil) - checkDeniedTables(t, sourceReplicaTablet, sourceKeyspace, []string{"/moving/", "view1"}) - checkDeniedTables(t, sourceRdOnlyTablet1, sourceKeyspace, []string{"/moving/", "view1"}) - checkDeniedTables(t, sourceRdOnlyTablet2, sourceKeyspace, []string{"/moving/", "view1"}) - checkClientConnRedirectionExecuteKeyrange(ctx, t, gconn, destinationKeyspace, []topodata.TabletType{topodata.TabletType_PRIMARY}, []string{"moving1", "moving2"}) - - // move replica back and forth - err = clusterInstance.VtctlclientProcess.ExecuteCommand("MigrateServedFrom", "--", "--reverse", "destination_keyspace/0", "replica") - require.NoError(t, err) - checkSrvKeyspaceServedFrom(t, cellj, destinationKeyspace, "ServedFrom(primary): source_keyspace\nServedFrom(replica): source_keyspace\n", *clusterInstance) - checkDeniedTables(t, sourcePrimaryTablet, sourceKeyspace, nil) - checkDeniedTables(t, sourceReplicaTablet, sourceKeyspace, nil) - checkDeniedTables(t, sourceRdOnlyTablet1, sourceKeyspace, []string{"/moving/", "view1"}) - checkDeniedTables(t, sourceRdOnlyTablet2, sourceKeyspace, []string{"/moving/", "view1"}) - - err = clusterInstance.VtctlclientProcess.ExecuteCommand("MigrateServedFrom", "destination_keyspace/0", "replica") - require.NoError(t, err) - checkSrvKeyspaceServedFrom(t, cellj, destinationKeyspace, "ServedFrom(primary): source_keyspace\n", *clusterInstance) - checkDeniedTables(t, sourcePrimaryTablet, sourceKeyspace, nil) - checkDeniedTables(t, sourceReplicaTablet, sourceKeyspace, []string{"/moving/", "view1"}) - checkDeniedTables(t, sourceRdOnlyTablet1, sourceKeyspace, []string{"/moving/", "view1"}) - checkDeniedTables(t, sourceRdOnlyTablet2, sourceKeyspace, []string{"/moving/", "view1"}) - checkClientConnRedirectionExecuteKeyrange(ctx, t, gconn, destinationKeyspace, []topodata.TabletType{topodata.TabletType_PRIMARY}, []string{"moving1", "moving2"}) - - // Cancel should fail now - err = clusterInstance.VtctlclientProcess.ExecuteCommand("CancelResharding", "destination_keyspace/0") - require.Error(t, err) - - // then serve primary from the destination shards - err = clusterInstance.VtctlclientProcess.ExecuteCommand("MigrateServedFrom", "destination_keyspace/0", "primary") - require.NoError(t, err) - checkSrvKeyspaceServedFrom(t, cellj, destinationKeyspace, "", *clusterInstance) - checkDeniedTables(t, sourcePrimaryTablet, sourceKeyspace, []string{"/moving/", "view1"}) - checkDeniedTables(t, sourceReplicaTablet, sourceKeyspace, []string{"/moving/", "view1"}) - checkDeniedTables(t, sourceRdOnlyTablet1, sourceKeyspace, []string{"/moving/", "view1"}) - checkDeniedTables(t, sourceRdOnlyTablet2, sourceKeyspace, []string{"/moving/", "view1"}) - - // check the binlog player is gone now - _ = destinationPrimaryTablet.VttabletProcess.WaitForBinLogPlayerCount(0) - - // check the stats are correct - checkStats(t) - - // now remove the tables on the source shard. The denied tables - // in the source shard won't match any table, make sure that works. - err = clusterInstance.VtctlclientProcess.ExecuteCommand("ApplySchema", "--", "--sql=drop view view1", "source_keyspace") - require.NoError(t, err) - - for _, table := range []string{"moving1", "moving2"} { - err = clusterInstance.VtctlclientProcess.ExecuteCommand("ApplySchema", "--", "--sql=drop table "+table, "source_keyspace") - require.NoError(t, err) - } - for _, tablet := range []cluster.Vttablet{sourcePrimaryTablet, sourceReplicaTablet, sourceRdOnlyTablet1, sourceRdOnlyTablet2} { - err = clusterInstance.VtctlclientProcess.ExecuteCommand("ReloadSchema", tablet.Alias) - require.NoError(t, err) - } - qr, _ = sourcePrimaryTablet.VttabletProcess.QueryTablet("select count(1) from staying1", sourceKeyspace, true) - assert.Equal(t, 1, len(qr.Rows), fmt.Sprintf("cannot read staying1: got %d", len(qr.Rows))) - - // test SetShardTabletControl - verifyVtctlSetShardTabletControl(t) - -} - -func verifyVtctlSetShardTabletControl(t *testing.T) { - // clear the rdonly entry: - err := clusterInstance.VtctlclientProcess.ExecuteCommand("SetShardTabletControl", "--", "--remove", "source_keyspace/0", "rdonly") - require.NoError(t, err) - assertTabletControls(t, clusterInstance, []topodata.TabletType{topodata.TabletType_PRIMARY, topodata.TabletType_REPLICA}) - - // re-add rdonly: - err = clusterInstance.VtctlclientProcess.ExecuteCommand("SetShardTabletControl", "--", "--denied_tables=/moving/,view1", "source_keyspace/0", "rdonly") - require.NoError(t, err) - assertTabletControls(t, clusterInstance, []topodata.TabletType{topodata.TabletType_PRIMARY, topodata.TabletType_REPLICA, topodata.TabletType_RDONLY}) - - //and then clear all entries: - err = clusterInstance.VtctlclientProcess.ExecuteCommand("SetShardTabletControl", "--", "--remove", "source_keyspace/0", "rdonly") - require.NoError(t, err) - err = clusterInstance.VtctlclientProcess.ExecuteCommand("SetShardTabletControl", "--", "--remove", "source_keyspace/0", "replica") - require.NoError(t, err) - err = clusterInstance.VtctlclientProcess.ExecuteCommand("SetShardTabletControl", "--", "--remove", "source_keyspace/0", "primary") - require.NoError(t, err) - - shardJSON, err := clusterInstance.VtctlclientProcess.ExecuteCommandWithOutput("GetShard", "source_keyspace/0") - require.NoError(t, err) - var shardJSONData topodata.Shard - err = json2.Unmarshal([]byte(shardJSON), &shardJSONData) - require.NoError(t, err) - assert.Empty(t, shardJSONData.TabletControls) - -} - -func assertTabletControls(t *testing.T, clusterInstance *cluster.LocalProcessCluster, aliases []topodata.TabletType) { - shardJSON, err := clusterInstance.VtctlclientProcess.ExecuteCommandWithOutput("GetShard", "source_keyspace/0") - require.NoError(t, err) - var shardJSONData topodata.Shard - err = json2.Unmarshal([]byte(shardJSON), &shardJSONData) - require.NoError(t, err) - assert.Equal(t, len(shardJSONData.TabletControls), len(aliases)) - for _, tc := range shardJSONData.TabletControls { - assert.Contains(t, aliases, tc.TabletType) - assert.Equal(t, []string{"/moving/", "view1"}, tc.DeniedTables) - } -} - -func checkStats(t *testing.T) { - - resultMap, err := clusterInstance.VtgateProcess.GetVars() - require.NoError(t, err) - resultVtTabletCall := resultMap["VttabletCall"] - resultVtTabletCallMap := resultVtTabletCall.(map[string]any) - resultHistograms := resultVtTabletCallMap["Histograms"] - resultHistogramsMap := resultHistograms.(map[string]any) - resultTablet := resultHistogramsMap["Execute.source_keyspace.0.replica"] - resultTableMap := resultTablet.(map[string]any) - resultCountStr := fmt.Sprintf("%v", reflect.ValueOf(resultTableMap["Count"])) - assert.Equal(t, "2", resultCountStr, fmt.Sprintf("unexpected value for VttabletCall(Execute.source_keyspace.0.replica) inside %s", resultCountStr)) - - // Verify primary reads done by self._check_client_conn_redirection(). - resultVtgateAPI := resultMap["VtgateApi"] - resultVtgateAPIMap := resultVtgateAPI.(map[string]any) - resultAPIHistograms := resultVtgateAPIMap["Histograms"] - resultAPIHistogramsMap := resultAPIHistograms.(map[string]any) - resultTabletDestination := resultAPIHistogramsMap["Execute.destination_keyspace.primary"] - resultTabletDestinationMap := resultTabletDestination.(map[string]any) - resultCountStrDestination := fmt.Sprintf("%v", reflect.ValueOf(resultTabletDestinationMap["Count"])) - assert.Equal(t, "6", resultCountStrDestination, fmt.Sprintf("unexpected value for VtgateApi(Execute.destination_keyspace.primary) inside %s)", resultCountStrDestination)) - - assert.Empty(t, resultMap["VtgateApiErrorCounts"]) - -} - -func insertInitialValues(t *testing.T, conn *mysql.Conn, sourcePrimaryTablet cluster.Vttablet, destinationPrimaryTablet cluster.Vttablet) { - moving1First = insertValues(t, conn, sourceKeyspace, "moving1", 100) - moving2First = insertValues(t, conn, sourceKeyspace, "moving2", 100) - staying1First = insertValues(t, conn, sourceKeyspace, "staying1", 100) - staying2First = insertValues(t, conn, sourceKeyspace, "staying2", 100) - checkValues(t, &sourcePrimaryTablet, sourceKeyspace, "vt_source_keyspace", "moving1", moving1First, 100) - checkValues(t, &sourcePrimaryTablet, sourceKeyspace, "vt_source_keyspace", "moving2", moving2First, 100) - checkValues(t, &sourcePrimaryTablet, sourceKeyspace, "vt_source_keyspace", "staying1", staying1First, 100) - checkValues(t, &sourcePrimaryTablet, sourceKeyspace, "vt_source_keyspace", "staying2", staying2First, 100) - checkValues(t, &sourcePrimaryTablet, sourceKeyspace, "vt_source_keyspace", "view1", moving1First, 100) - - moving3NoPkFirst = insertValues(t, conn, sourceKeyspace, "moving3_no_pk", 100) - checkValues(t, &sourcePrimaryTablet, sourceKeyspace, "vt_source_keyspace", "moving3_no_pk", moving3NoPkFirst, 100) - - // Insert data directly because vtgate would redirect us. - _, err := destinationPrimaryTablet.VttabletProcess.QueryTablet(fmt.Sprintf("insert into %s (id, msg) values(%d, 'value %d')", "extra1", 1, 1), destinationKeyspace, true) - require.NoError(t, err) - checkValues(t, &destinationPrimaryTablet, destinationKeyspace, "vt_destination_keyspace", "extra1", 1, 1) -} - -func checkClientConnRedirectionExecuteKeyrange(ctx context.Context, t *testing.T, conn *vtgateconn.VTGateConn, keyspace string, servedFromDbTypes []topodata.TabletType, movedTables []string) { - // check that the ServedFrom indirection worked correctly. - for _, tabletType := range servedFromDbTypes { - session := conn.Session(fmt.Sprintf("%s@%v", keyspace, tabletType), nil) - for _, table := range movedTables { - _, err := session.Execute(ctx, fmt.Sprintf("select * from %s", table), nil) - require.NoError(t, err) - } - } -} - -func checkValues(t *testing.T, tablet *cluster.Vttablet, keyspace string, dbname string, table string, first int, count int) { - t.Helper() - log.Infof("Checking %d values from %s/%s starting at %d", count, dbname, table, first) - qr, _ := tablet.VttabletProcess.QueryTablet(fmt.Sprintf("select id, msg from %s where id>=%d order by id limit %d", table, first, count), keyspace, true) - assert.Equal(t, count, len(qr.Rows), fmt.Sprintf("got wrong number of rows: %d != %d", len(qr.Rows), count)) - i := 0 - for i < count { - result, _ := evalengine.ToInt64(qr.Rows[i][0]) - assert.Equal(t, int64(first+i), result, fmt.Sprintf("got wrong number of rows: %d != %d", len(qr.Rows), first+i)) - assert.Contains(t, qr.Rows[i][1].String(), fmt.Sprintf("value %d", first+i), fmt.Sprintf("invalid msg[%d]: 'value %d' != '%s'", i, first+i, qr.Rows[i][1].String())) - i++ - } -} - -func checkValuesTimeout(t *testing.T, tablet cluster.Vttablet, table string, first int, count int, timeoutInSec int) bool { - t.Helper() - for i := 0; i < timeoutInSec; i-- { - qr, err := tablet.VttabletProcess.QueryTablet(fmt.Sprintf("select id, msg from %s where id>=%d order by id limit %d", table, first, count), destinationKeyspace, true) - if err != nil { - require.NoError(t, err, "select failed on primary tablet") - } - if len(qr.Rows) == count { - return true - } - time.Sleep(1 * time.Second) - } - return true -} - -func checkDeniedTables(t *testing.T, tablet cluster.Vttablet, keyspace string, expected []string) { - t.Helper() - tabletStatus := tablet.VttabletProcess.GetStatus() - if expected != nil { - assert.Contains(t, tabletStatus, fmt.Sprintf("DeniedTables: %s", strings.Join(expected, " "))) - } else { - assert.NotContains(t, tabletStatus, "DeniedTables") - } - - // check we can or cannot access the tables - for _, table := range []string{"moving1", "moving2"} { - if expected != nil && strings.Contains(strings.Join(expected, " "), "moving") { - // table is denied, should get error - err := clusterInstance.VtctlclientProcess.ExecuteCommand("VtTabletExecute", "--", "--json", tablet.Alias, fmt.Sprintf("select count(1) from %s", table)) - require.Error(t, err, "disallowed due to rule: enforce denied tables") - } else { - // table is not part of the denylist, should just work - _, err := tablet.VttabletProcess.QueryTablet(fmt.Sprintf("select count(1) from %s", table), keyspace, true) - require.NoError(t, err) - } - } -} - -func insertValues(t *testing.T, conn *mysql.Conn, keyspace string, table string, count int) int { - result := insertIndex - i := 0 - for i < count { - execQuery(t, conn, "begin") - execQuery(t, conn, "use `"+keyspace+":0`") - execQuery(t, conn, fmt.Sprintf("insert into %s (id, msg) values(%d, 'value %d')", table, insertIndex, insertIndex)) - execQuery(t, conn, "commit") - insertIndex++ - i++ - } - return result -} - -func teardownCluster() { - clusterInstance.Teardown() -} - -func execQuery(t *testing.T, conn *mysql.Conn, query string) *sqltypes.Result { - t.Helper() - qr, err := conn.ExecuteFetch(query, 1000, true) - require.NoError(t, err) - return qr -} - -// CheckSrvKeyspaceServedFrom verifies the servedFrom with expected -func checkSrvKeyspaceServedFrom(t *testing.T, cell string, ksname string, expected string, ci cluster.LocalProcessCluster) { - srvKeyspace := sharding.GetSrvKeyspace(t, cell, ksname, ci) - tabletTypeKeyspaceMap := make(map[string]string) - result := "" - if srvKeyspace.GetServedFrom() != nil { - for _, servedFrom := range srvKeyspace.GetServedFrom() { - tabletTy := strings.ToLower(servedFrom.GetTabletType().String()) - tabletTypeKeyspaceMap[tabletTy] = servedFrom.GetKeyspace() - } - } - if tabletTypeKeyspaceMap["primary"] != "" { - result = result + fmt.Sprintf("ServedFrom(%s): %s\n", "primary", tabletTypeKeyspaceMap["primary"]) - } - if tabletTypeKeyspaceMap["rdonly"] != "" { - result = result + fmt.Sprintf("ServedFrom(%s): %s\n", "rdonly", tabletTypeKeyspaceMap["rdonly"]) - } - if tabletTypeKeyspaceMap["replica"] != "" { - result = result + fmt.Sprintf("ServedFrom(%s): %s\n", "replica", tabletTypeKeyspaceMap["replica"]) - } - assert.Equal(t, expected, result, fmt.Sprintf("Mismatch in srv keyspace for cell %s keyspace %s, expected:\n %s\ngot:\n%s", cell, ksname, expected, result)) -} - -func initializeCluster() (int, error) { - var mysqlProcesses []*exec.Cmd - clusterInstance = cluster.NewCluster(cellj, hostname) - clusterInstance.VtGatePlannerVersion = planbuilder.Gen4 - - // Start topo server - if err := clusterInstance.StartTopo(); err != nil { - return 1, err - } - - for _, keyspaceStr := range []string{sourceKeyspace, destinationKeyspace} { - KeyspacePtr := &cluster.Keyspace{Name: keyspaceStr} - keyspace := *KeyspacePtr - if keyspaceStr == sourceKeyspace { - if err := clusterInstance.VtctlProcess.CreateKeyspace(keyspace.Name); err != nil { - return 1, err - } - } else { - if err := clusterInstance.VtctlclientProcess.ExecuteCommand("CreateKeyspace", "--", "--served_from", "primary:source_keyspace,replica:source_keyspace,rdonly:source_keyspace", "destination_keyspace"); err != nil { - return 1, err - } - } - shard := &cluster.Shard{ - Name: shardName, - } - for i := 0; i < 4; i++ { - // instantiate vttablet object with reserved ports - tabletUID := clusterInstance.GetAndReserveTabletUID() - var tablet *cluster.Vttablet - if i == 0 { - tablet = clusterInstance.NewVttabletInstance("replica", tabletUID, cellj) - } else if i == 1 { - tablet = clusterInstance.NewVttabletInstance("replica", tabletUID, cellj) - } else { - tablet = clusterInstance.NewVttabletInstance("rdonly", tabletUID, cellj) - } - // Start Mysqlctl process - tablet.MysqlctlProcess = *cluster.MysqlCtlProcessInstance(tablet.TabletUID, tablet.MySQLPort, clusterInstance.TmpDirectory) - proc, err := tablet.MysqlctlProcess.StartProcess() - if err != nil { - return 1, err - } - mysqlProcesses = append(mysqlProcesses, proc) - // start vttablet process - tablet.VttabletProcess = cluster.VttabletProcessInstance( - tablet.HTTPPort, - tablet.GrpcPort, - tablet.TabletUID, - clusterInstance.Cell, - shardName, - keyspace.Name, - clusterInstance.VtctldProcess.Port, - tablet.Type, - clusterInstance.TopoProcess.Port, - clusterInstance.Hostname, - clusterInstance.TmpDirectory, - clusterInstance.VtTabletExtraArgs, - clusterInstance.EnableSemiSync, - clusterInstance.DefaultCharset) - tablet.Alias = tablet.VttabletProcess.TabletPath - - shard.Vttablets = append(shard.Vttablets, tablet) - } - keyspace.Shards = append(keyspace.Shards, *shard) - clusterInstance.Keyspaces = append(clusterInstance.Keyspaces, keyspace) - } - for _, proc := range mysqlProcesses { - err := proc.Wait() - if err != nil { - return 1, err - } - } - return 0, nil -} - -func validateKeyspaceJSON(t *testing.T, keyspaceJSON string, cellsArr []string) { - var keyspace topodata.Keyspace - err := json2.Unmarshal([]byte(keyspaceJSON), &keyspace) - require.NoError(t, err) - found := false - for _, servedFrom := range keyspace.GetServedFroms() { - if strings.ToLower(servedFrom.GetTabletType().String()) == "rdonly" { - found = true - if cellsArr != nil { - if len(cellsArr) > 0 { - for _, eachCell := range cellsArr { - assert.Contains(t, strings.Join(servedFrom.GetCells(), " "), eachCell) - } - } else { - assert.Empty(t, servedFrom.GetCells()) - } - } - } - } - if cellsArr != nil { - assert.Equal(t, true, found) - } else { - assert.Equal(t, false, found) - } -} diff --git a/go/test/endtoend/tabletmanager/tablet_health_test.go b/go/test/endtoend/tabletmanager/tablet_health_test.go index 88573da9464..49c1e374742 100644 --- a/go/test/endtoend/tabletmanager/tablet_health_test.go +++ b/go/test/endtoend/tabletmanager/tablet_health_test.go @@ -265,10 +265,8 @@ func TestHealthCheckDrainedStateDoesNotShutdownQueryService(t *testing.T) { checkHealth(t, rdonlyTablet.HTTPPort, false) assert.Equal(t, "SERVING", rdonlyTablet.VttabletProcess.GetTabletStatus()) - // Change from rdonly to drained and stop replication. (These - // actions are similar to the SplitClone vtworker command - // implementation.) The tablet will stay healthy, and the - // query service is still running. + // Change from rdonly to drained and stop replication. The tablet will stay + // healthy, and the query service is still running. err = clusterInstance.VtctlclientProcess.ExecuteCommand("ChangeTabletType", rdonlyTablet.Alias, "drained") require.NoError(t, err) // Trying to drain the same tablet again, should error diff --git a/go/test/endtoend/worker/worker_test.go b/go/test/endtoend/worker/worker_test.go deleted file mode 100644 index cb4dc6aa79e..00000000000 --- a/go/test/endtoend/worker/worker_test.go +++ /dev/null @@ -1,606 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Tests the robustness and resiliency of vtworkers. - -package worker - -import ( - "fmt" - "io" - "net/http" - "net/url" - "os/exec" - "reflect" - "strconv" - "strings" - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "vitess.io/vitess/go/json2" - "vitess.io/vitess/go/test/endtoend/cluster" - "vitess.io/vitess/go/test/endtoend/sharding" - - topodatapb "vitess.io/vitess/go/vt/proto/topodata" -) - -var ( - primary *cluster.Vttablet - replica1 *cluster.Vttablet - rdOnly1 *cluster.Vttablet - - shard0Primary *cluster.Vttablet - shard0Replica *cluster.Vttablet - shard0RdOnly1 *cluster.Vttablet - - shard1Primary *cluster.Vttablet - shard1Replica *cluster.Vttablet - shard1RdOnly1 *cluster.Vttablet - - localCluster *cluster.LocalProcessCluster - cell = cluster.DefaultCell - hostname = "localhost" - keyspaceName = "test_keyspace" - shardName = "0" - shardTablets []*cluster.Vttablet - shard0Tablets []*cluster.Vttablet - shard1Tablets []*cluster.Vttablet - workerTestOffset = 0 - commonTabletArg = []string{ - "--binlog_use_v3_resharding_mode=true"} - vtWorkerTest = ` - create table worker_test ( - id bigint unsigned, - sid int unsigned, - msg varchar(64), - primary key (id), - index by_msg (msg) - ) Engine=InnoDB` - - vSchema = ` - { - "sharded": true, - "vindexes": { - "hash": { - "type": "hash" - } - }, - "tables": { - "worker_test": { - "column_vindexes": [ - { - "column": "sid", - "name": "hash" - } - ] - } - } -}` -) - -func TestReparentDuringWorkerCopy(t *testing.T) { - defer cluster.PanicHandler(t) - _, err := initializeCluster(t, false) - defer localCluster.Teardown() - require.Nil(t, err) - initialSetup(t) - verifySuccessfulWorkerCopyWithReparent(t, false) -} - -func TestReparentDuringWorkerCopyMysqlDown(t *testing.T) { - defer cluster.PanicHandler(t) - _, err := initializeCluster(t, false) - defer localCluster.Teardown() - require.Nil(t, err) - initialSetup(t) - verifySuccessfulWorkerCopyWithReparent(t, true) -} - -func TestWebInterface(t *testing.T) { - defer cluster.PanicHandler(t) - _, err := initializeCluster(t, true) - require.Nil(t, err) - defer localCluster.Teardown() - err = localCluster.StartVtworker(cell, "--use_v3_resharding_mode=true") - assert.Nil(t, err) - baseURL := fmt.Sprintf("http://localhost:%d", localCluster.VtworkerProcess.Port) - - // Wait for /status to become available. - startTime := time.Now() - for { - resp, err := http.Get(baseURL + "/status") - if err != nil && !time.Now().After(startTime.Add(10*time.Second)) { - time.Sleep(10 * time.Millisecond) - continue - } - if resp.StatusCode == 200 || time.Now().After(startTime.Add(10*time.Second)) { - break - } - } - - // Run the command twice to make sure it's idempotent. - i := 0 - for i < 2 { - data := url.Values{"message": {"pong"}} - http.DefaultClient.CheckRedirect = func(req *http.Request, via []*http.Request) error { - return http.ErrUseLastResponse - } - resp, err := http.Post(baseURL+"/Debugging/Ping", "application/x-www-form-urlencoded", strings.NewReader(data.Encode())) - assert.Nil(t, err) - assert.Equal(t, 307, resp.StatusCode) - - // Wait for the Ping command to finish. - pollForVars(t, "done") - // Verify that the command logged something and it's available at /status. - resp, err = http.Get(baseURL + "/status") - assert.Nil(t, err) - if resp.StatusCode == 200 { - respByte, _ := io.ReadAll(resp.Body) - respStr := string(respByte) - assert.Contains(t, respStr, "Ping command was called with message: 'pong'", fmt.Sprintf("Command did not log output to /status: %s", respStr)) - } - - // Reset the job. - _, err = http.Get(baseURL + "/reset") - assert.Nil(t, err) - resp, err = http.Get(baseURL + "/status") - assert.Nil(t, err) - if resp.StatusCode == 200 { - respByte, _ := io.ReadAll(resp.Body) - statusAfterReset := string(respByte) - assert.Contains(t, statusAfterReset, "This worker is idle.", "/status does not indicate that the reset was successful") - } - i++ - } - - err = localCluster.VtworkerProcess.TearDown() - assert.Nil(t, err) - -} - -func initialSetup(t *testing.T) { - - runShardTablets(t, "0", shardTablets, true) - - // create the split shards - runShardTablets(t, "-80", shard0Tablets, false) - runShardTablets(t, "80-", shard1Tablets, false) - - // insert values - insertValues(primary, "shard-0", 1, 4000, 0) - insertValues(primary, "shard-1", 4, 4000, 1) - - // wait for replication position - cluster.WaitForReplicationPos(t, primary, rdOnly1, "localhost", 60) - - copySchemaToDestinationShard(t) -} - -func verifySuccessfulWorkerCopyWithReparent(t *testing.T, isMysqlDown bool) { - - // Verifies that vtworker can successfully copy data for a SplitClone. - - // Order of operations: - // 1. Run a background vtworker - // 2. Wait until the worker successfully resolves the destination primaries. - // 3. Reparent the destination tablets - // 4. Wait until the vtworker copy is finished - // 5. Verify that the worker was forced to reresolve topology and retry writes - // due to the reparent. - // 6. Verify that the data was copied successfully to both new shards - - err := localCluster.StartVtworker(cell, "--use_v3_resharding_mode=true") - assert.Nil(t, err) - - // --max_tps is only specified to enable the throttler and ensure that the - // code is executed. But the intent here is not to throttle the test, hence - // the rate limit is set very high. - - var args []string - - args = append(args, "SplitClone", "--offline=false", - "--destination_writer_count", "1", - "--min_healthy_rdonly_tablets", "1", - "--max_tps", "9999") - - // --chunk_count is 2 because rows are currently ordered by primary key such - // that all rows of the first shard come first and then the second shard. - // Make the clone as slow as necessary such that there is enough time to - // run PlannedReparent in the meantime. - - args = append(args, "--source_reader_count", "2", - "--chunk_count", "2", - "--min_rows_per_chunk", "1", - "--write_query_max_rows", "1", - "test_keyspace/0") - - proc, err := localCluster.VtworkerProcess.ExecuteCommandInBg(args...) - assert.Nil(t, err) - - if isMysqlDown { - // vtworker is blocked at this point. This is a good time to test that its - // throttler server is reacting to RPCs. - sharding.CheckThrottlerService(t, fmt.Sprintf("%s:%d", hostname, localCluster.VtworkerProcess.GrpcPort), - []string{"test_keyspace/-80", "test_keyspace/80-"}, 9999, *localCluster) - - pollForVars(t, "cloning the data (online)") - - // Stop MySql - var mysqlCtlProcessList []*exec.Cmd - - for _, tablet := range []*cluster.Vttablet{shard0Primary, shard1Primary} { - tablet.MysqlctlProcess.InitMysql = false - sqlProc, err := tablet.MysqlctlProcess.StopProcess() - assert.Nil(t, err) - mysqlCtlProcessList = append(mysqlCtlProcessList, sqlProc) - } - - // Wait for mysql processes to stop - for _, sqlProc := range mysqlCtlProcessList { - if err := sqlProc.Wait(); err != nil { - t.Fatal(err) - } - } - - // If MySQL is down, we wait until vtworker retried at least once to make - // sure it reached the point where a write failed due to MySQL being down. - // There should be two retries at least, one for each destination shard. - pollForVarsWorkerRetryCount(t, 2) - - // Bring back primaries. Since we test with semi-sync now, we need at least - // one replica for the new primary. This test is already quite expensive, - // so we bring back the old primary and then let it be converted to a - // replica by PRS, rather than leaving the old primary down and having a - // third replica up the whole time. - - // start mysql - var mysqlCtlProcessStartList []*exec.Cmd - - for _, tablet := range []*cluster.Vttablet{shard0Primary, shard1Primary} { - tablet.MysqlctlProcess.InitMysql = false - sqlProc, err := tablet.MysqlctlProcess.StartProcess() - assert.Nil(t, err) - mysqlCtlProcessStartList = append(mysqlCtlProcessStartList, sqlProc) - } - - // Wait for mysql processes to start - for _, sqlProc := range mysqlCtlProcessStartList { - if err := sqlProc.Wait(); err != nil { - t.Fatal(err) - } - } - } else { - - // NOTE: There is a race condition around this: - // It's possible that the SplitClone vtworker command finishes before the - // PlannedReparentShard vtctl command, which we start below, succeeds. - // Then the test would fail because vtworker did not have to retry. - // - // To workaround this, the test takes a parameter to increase the number of - // rows that the worker has to copy (with the idea being to slow the worker - // down). - // You should choose a value for num_insert_rows, such that this test - // passes for your environment (trial-and-error...) - // Make sure that vtworker got past the point where it picked a primary - // for each destination shard ("finding targets" state). - pollForVars(t, "cloning the data (online)") - - } - - // Reparent away from the old primaries. - localCluster.VtctlclientProcess.ExecuteCommand("PlannedReparentShard", "--", "--keyspace_shard", - "test_keyspace/-80", "--new_primary", shard0Replica.Alias) - localCluster.VtctlclientProcess.ExecuteCommand("PlannedReparentShard", "--", "--keyspace_shard", - "test_keyspace/80-", "--new_primary", shard1Replica.Alias) - - proc.Wait() - - // Verify that we were forced to re-resolve and retry. - pollForVarsWorkerRetryCount(t, 1) - - err = localCluster.VtworkerProcess.TearDown() - assert.Nil(t, err) - - cluster.WaitForReplicationPos(t, shard0Replica, shard0RdOnly1, "localhost", 60) - cluster.WaitForReplicationPos(t, shard1Replica, shard1RdOnly1, "localhost", 60) - - err = localCluster.VtworkerProcess.ExecuteVtworkerCommand(localCluster.GetAndReservePort(), localCluster.GetAndReservePort(), "--cell", cell, - "--use_v3_resharding_mode=true", - "SplitClone", "--", - "--online=false", - "--min_healthy_rdonly_tablets", "1", - "test_keyspace/0") - assert.Nil(t, err) - - // Make sure that everything is caught up to the same replication point - runSplitDiff(t, "test_keyspace/-80") - runSplitDiff(t, "test_keyspace/80-") - assertShardDataEqual(t, "0", primary, shard0Replica) - assertShardDataEqual(t, "1", primary, shard1Replica) -} - -func assertShardDataEqual(t *testing.T, shardNum string, sourceTablet *cluster.Vttablet, destinationTablet *cluster.Vttablet) { - messageStr := fmt.Sprintf("shard-%s", shardNum) - selectQuery := "select id,sid,msg from worker_test where msg = '" + messageStr + "' order by id asc" - qrSource, err := sourceTablet.VttabletProcess.QueryTablet(selectQuery, keyspaceName, true) - assert.Nil(t, err) - - // Make sure all the right rows made it from the source to the destination - qrDestination, err := destinationTablet.VttabletProcess.QueryTablet(selectQuery, keyspaceName, true) - assert.Nil(t, err) - assert.Equal(t, len(qrSource.Rows), len(qrDestination.Rows)) - - assert.Equal(t, fmt.Sprint(qrSource.Rows), fmt.Sprint(qrDestination.Rows)) - - // Make sure that there are no extra rows on the destination - countQuery := "select count(*) from worker_test" - qrDestinationCount, err := destinationTablet.VttabletProcess.QueryTablet(countQuery, keyspaceName, true) - assert.Nil(t, err) - assert.Equal(t, fmt.Sprintf("%d", len(qrDestination.Rows)), qrDestinationCount.Rows[0][0].ToString()) - -} - -// Runs a vtworker SplitDiff on the given keyspace/shard. -func runSplitDiff(t *testing.T, keyspaceShard string) { - - err := localCluster.VtworkerProcess.ExecuteVtworkerCommand(localCluster.GetAndReservePort(), - localCluster.GetAndReservePort(), - "--cell", cell, - "--use_v3_resharding_mode=true", - "SplitDiff", "--", - "--min_healthy_rdonly_tablets", "1", - keyspaceShard) - assert.Nil(t, err) - -} - -func pollForVars(t *testing.T, mssg string) { - startTime := time.Now() - var resultMap map[string]any - var err error - var workerState string - for { - resultMap, err = localCluster.VtworkerProcess.GetVars() - assert.Nil(t, err) - workerState = fmt.Sprintf("%v", reflect.ValueOf(resultMap["WorkerState"])) - if strings.Contains(workerState, mssg) || (time.Now().After(startTime.Add(60 * time.Second))) { - break - } - continue - } - assert.Contains(t, workerState, mssg) -} - -func pollForVarsWorkerRetryCount(t *testing.T, count int) { - startTime := time.Now() - var resultMap map[string]any - var err error - var workerRetryCountInt int - for { - resultMap, err = localCluster.VtworkerProcess.GetVars() - if err != nil { - continue - } - workerRetryCount := fmt.Sprintf("%v", reflect.ValueOf(resultMap["WorkerRetryCount"])) - workerRetryCountInt, err = strconv.Atoi(workerRetryCount) - assert.Nil(t, err) - if workerRetryCountInt > count || (time.Now().After(startTime.Add(60 * time.Second))) { - break - } - continue - } - assert.GreaterOrEqual(t, workerRetryCountInt, count) -} - -// vttablet: the Tablet instance to modify. -// msg: the value of `msg` column. -// numValues: number of rows to be inserted. -func insertValues(tablet *cluster.Vttablet, msg string, sid int, numValues int, initialVal int) { - - // For maximum performance, multiple values are inserted in one statement. - // However, when the statements are too long, queries will timeout and - // vttablet will kill them. Therefore, we chunk it into multiple statements. - maxChunkSize := 100 * 1000 - - var fullList []int - for i := 1; i <= numValues; i++ { - fullList = append(fullList, i) - } - m := getChunkArr(fullList, maxChunkSize) - - for i := 0; i < len(m); i++ { - valueStr := "" - for j := 0; j < len(m[i]); j++ { - if m[i][j] != m[i][0] { - valueStr += "," - } - rowID := j*2 + initialVal - valueStr = valueStr + fmt.Sprintf("(%d, %d, '%s')", rowID, sid, msg) - } - workerTestOffset += len(m[i]) - tablet.VttabletProcess.QueryTablet(fmt.Sprintf("insert into worker_test(id, sid, msg) values %s", valueStr), keyspaceName, true) - } -} - -func getChunkArr(fullList []int, chunkSize int) [][]int { - var m [][]int - for i := 0; i < len(fullList); i += chunkSize { - end := i + chunkSize - if end > len(fullList) { - end = len(fullList) - } - m = append(m, fullList[i:end]) - } - return m -} - -// shardName: the name of the shard to start tablets in -// tabletArr: an instance of ShardTablets for the given shard -// createTable: boolean, True iff we should create a table on the tablets -func runShardTablets(t *testing.T, shardName string, tabletArr []*cluster.Vttablet, createTable bool) error { - //Handles all the necessary work for initially running a shard's tablets. - - // This encompasses the following steps: - // 1. (optional) Create db - // 2. Starting vttablets and let themselves init them - // 3. Waiting for the appropriate vttablet state - // 4. Force reparent to the primary tablet - // 5. RebuildKeyspaceGraph - // 7. (optional) Running initial schema setup - - // Start tablets. - for _, tablet := range tabletArr { - if err := tablet.VttabletProcess.CreateDB(keyspaceName); err != nil { - return err - } - err := tablet.VttabletProcess.Setup() - require.Nil(t, err) - } - - // Reparent to choose an initial primary and enable replication. - err := localCluster.VtctlclientProcess.InitShardPrimary(keyspaceName, shardName, cell, tabletArr[0].TabletUID) - require.Nil(t, err) - - for { - result, err := localCluster.VtctlclientProcess.ExecuteCommandWithOutput("GetShard", fmt.Sprintf("test_keyspace/%s", shardName)) - assert.Nil(t, err) - - var shardInfo topodatapb.Shard - err = json2.Unmarshal([]byte(result), &shardInfo) - assert.Nil(t, err) - - if int(shardInfo.PrimaryAlias.Uid) == tabletArr[0].TabletUID { - break - } - time.Sleep(10 * time.Second) - continue - } - - err = localCluster.VtctlclientProcess.ExecuteCommand("RebuildKeyspaceGraph", "test_keyspace") - assert.Nil(t, err) - - // Enforce a health check instead of waiting for the next periodic one. - // (saves up to 1 second execution time on average) - for _, tablet := range []*cluster.Vttablet{tabletArr[1], tabletArr[2]} { - err = localCluster.VtctlclientProcess.ExecuteCommand("RunHealthCheck", tablet.Alias) - assert.Nil(t, err) - } - - // Wait for tablet state to change after starting all tablets. This allows - // us to start all tablets at once, instead of sequentially waiting. - // NOTE: Replication has to be enabled first or the health check will - // set a replica or rdonly tablet back to NOT_SERVING. - - for _, tablet := range tabletArr { - err = tablet.VttabletProcess.WaitForTabletStatus("SERVING") - require.Nil(t, err) - } - - if createTable { - err = localCluster.VtctlclientProcess.ApplySchema(keyspaceName, vtWorkerTest) - assert.Nil(t, err) - - err = localCluster.VtctlclientProcess.ApplyVSchema(keyspaceName, vSchema) - assert.Nil(t, err) - } - - return err -} - -func copySchemaToDestinationShard(t *testing.T) { - for _, keyspaceShard := range []string{"test_keyspace/-80", "test_keyspace/80-"} { - err := localCluster.VtctlclientProcess.ExecuteCommand("CopySchemaShard", "--", "--exclude_tables", "unrelated", "test_keyspace/0", keyspaceShard) - assert.Nil(t, err) - } -} - -func initializeCluster(t *testing.T, onlyTopo bool) (int, error) { - - localCluster = cluster.NewCluster(cell, hostname) - - // Start topo server - err := localCluster.StartTopo() - if err != nil { - return 1, err - } - - if onlyTopo { - return 0, nil - } - // Start keyspace - keyspace := &cluster.Keyspace{ - Name: keyspaceName, - } - localCluster.Keyspaces = append(localCluster.Keyspaces, *keyspace) - - shard := &cluster.Shard{ - Name: shardName, - } - shard0 := &cluster.Shard{ - Name: "-80", - } - shard1 := &cluster.Shard{ - Name: "80-", - } - - // Defining all the tablets - primary = localCluster.NewVttabletInstance("replica", 0, "") - replica1 = localCluster.NewVttabletInstance("replica", 0, "") - rdOnly1 = localCluster.NewVttabletInstance("rdonly", 0, "") - shard0Primary = localCluster.NewVttabletInstance("replica", 0, "") - shard0Replica = localCluster.NewVttabletInstance("replica", 0, "") - shard0RdOnly1 = localCluster.NewVttabletInstance("rdonly", 0, "") - shard1Primary = localCluster.NewVttabletInstance("replica", 0, "") - shard1Replica = localCluster.NewVttabletInstance("replica", 0, "") - shard1RdOnly1 = localCluster.NewVttabletInstance("rdonly", 0, "") - - shard.Vttablets = []*cluster.Vttablet{primary, replica1, rdOnly1} - shard0.Vttablets = []*cluster.Vttablet{shard0Primary, shard0Replica, shard0RdOnly1} - shard1.Vttablets = []*cluster.Vttablet{shard1Primary, shard1Replica, shard1RdOnly1} - - localCluster.VtTabletExtraArgs = append(localCluster.VtTabletExtraArgs, commonTabletArg...) - - err = localCluster.SetupCluster(keyspace, []cluster.Shard{*shard, *shard0, *shard1}) - assert.Nil(t, err) - - // Start MySql - var mysqlCtlProcessList []*exec.Cmd - for _, shard := range localCluster.Keyspaces[0].Shards { - for _, tablet := range shard.Vttablets { - if proc, err := tablet.MysqlctlProcess.StartProcess(); err != nil { - t.Fatal(err) - } else { - mysqlCtlProcessList = append(mysqlCtlProcessList, proc) - } - } - } - - // Wait for mysql processes to start - for _, proc := range mysqlCtlProcessList { - if err := proc.Wait(); err != nil { - t.Fatal(err) - } - } - - shardTablets = []*cluster.Vttablet{primary, replica1, rdOnly1} - shard0Tablets = []*cluster.Vttablet{shard0Primary, shard0Replica, shard0RdOnly1} - shard1Tablets = []*cluster.Vttablet{shard1Primary, shard1Replica, shard1RdOnly1} - - return 0, nil -} diff --git a/go/test/fuzzing/autogenerate/convert_grep_to_fuzzer.go b/go/test/fuzzing/autogenerate/convert_grep_to_fuzzer.go index 24b00b52dbb..0f9da130f2d 100644 --- a/go/test/fuzzing/autogenerate/convert_grep_to_fuzzer.go +++ b/go/test/fuzzing/autogenerate/convert_grep_to_fuzzer.go @@ -53,7 +53,6 @@ var importPathShort = map[string]string{ "vitess.io/vitess/go/vt/proto/vschema": "vschema", "vitess.io/vitess/go/vt/proto/mysqlctl": "mysqlctl", "vitess.io/vitess/go/vt/proto/vtadmin": "vtadmin", - "vitess.io/vitess/go/vt/proto/vtworkerdata": "vtworkerdata", "vitess.io/vitess/go/vt/proto/throttlerdata": "throttlerdata", "vitess.io/vitess/go/vt/proto/topodata": "topodata", } @@ -77,7 +76,6 @@ var pathToImportPath = map[string]string{ "./proto/vschema/vschema_vtproto.pb.go": "vitess.io/vitess/go/vt/proto/vschema", "./proto/mysqlctl/mysqlctl_vtproto.pb.go": "vitess.io/vitess/go/vt/proto/mysqlctl", "./proto/vtadmin/vtadmin_vtproto.pb.go": "vitess.io/vitess/go/vt/proto/vtadmin", - "./proto/vtworkerdata/vtworkerdata_vtproto.pb.go": "vitess.io/vitess/go/vt/proto/vtworkerdata", "./proto/throttlerdata/throttlerdata_vtproto.pb.go": "vitess.io/vitess/go/vt/proto/throttlerdata", "./proto/topodata/topodata_vtproto.pb.go": "vitess.io/vitess/go/vt/proto/topodata", } diff --git a/go/test/fuzzing/vtctl_fuzzer.go b/go/test/fuzzing/vtctl_fuzzer.go index 59da236f7fe..dfe18785a8a 100644 --- a/go/test/fuzzing/vtctl_fuzzer.go +++ b/go/test/fuzzing/vtctl_fuzzer.go @@ -90,8 +90,6 @@ func getCommandType(index int) string { 45: "RemoveKeyspaceCell", 46: "GetKeyspace", 47: "GetKeyspaces", - 48: "SetKeyspaceShardingInfo", - 49: "SetKeyspaceServedFrom", 50: "RebuildKeyspaceGraph", 51: "ValidateKeyspace", 52: "Reshard", @@ -100,17 +98,10 @@ func getCommandType(index int) string { 55: "CreateLookupVindex", 56: "ExternalizeVindex", 57: "Materialize", - 58: "SplitClone", - 59: "VerticalSplitClone", 60: "VDiff", - 61: "MigrateServedTypes", - 62: "MigrateServedFrom", 63: "SwitchReads", 64: "SwitchWrites", - 65: "CancelResharding", - 66: "ShowResharding", 67: "FindAllShardsInKeyspace", - 68: "WaitForDrain", } return m[index] diff --git a/go/vt/automation/cluster_operation_instance.go b/go/vt/automation/cluster_operation_instance.go deleted file mode 100644 index e7ff59d283e..00000000000 --- a/go/vt/automation/cluster_operation_instance.go +++ /dev/null @@ -1,64 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package automation - -import ( - "google.golang.org/protobuf/proto" - - automationpb "vitess.io/vitess/go/vt/proto/automation" -) - -// ClusterOperationInstance is a runtime type which enhances the protobuf message "ClusterOperation" with runtime specific data. -// Unlike the protobuf message, the additional runtime data will not be part of a checkpoint. -// Methods of this struct are not thread-safe. -type ClusterOperationInstance struct { - *automationpb.ClusterOperation - taskIDGenerator *IDGenerator -} - -// NewClusterOperationInstance creates a new cluster operation instance with one initial task. -func NewClusterOperationInstance(clusterOpID string, initialTask *automationpb.TaskContainer, taskIDGenerator *IDGenerator) ClusterOperationInstance { - c := ClusterOperationInstance{ - &automationpb.ClusterOperation{ - Id: clusterOpID, - SerialTasks: []*automationpb.TaskContainer{}, - State: automationpb.ClusterOperationState_CLUSTER_OPERATION_NOT_STARTED, - }, - taskIDGenerator, - } - c.InsertTaskContainers([]*automationpb.TaskContainer{initialTask}, 0) - return c -} - -// InsertTaskContainers inserts "newTaskContainers" at pos in the current list of task containers. Existing task containers will be moved after the new task containers. -func (c *ClusterOperationInstance) InsertTaskContainers(newTaskContainers []*automationpb.TaskContainer, pos int) { - AddMissingTaskID(newTaskContainers, c.taskIDGenerator) - - newSerialTasks := make([]*automationpb.TaskContainer, len(c.SerialTasks)+len(newTaskContainers)) - copy(newSerialTasks, c.SerialTasks[:pos]) - copy(newSerialTasks[pos:], newTaskContainers) - copy(newSerialTasks[pos+len(newTaskContainers):], c.SerialTasks[pos:]) - c.SerialTasks = newSerialTasks -} - -// Clone creates a deep copy of the inner protobuf. -// Other elements e.g. taskIDGenerator are not deep-copied. -func (c ClusterOperationInstance) Clone() ClusterOperationInstance { - var clone = c - clone.ClusterOperation = proto.Clone(c.ClusterOperation).(*automationpb.ClusterOperation) - return clone -} diff --git a/go/vt/automation/copy_schema_shard_task.go b/go/vt/automation/copy_schema_shard_task.go deleted file mode 100644 index 907174d2489..00000000000 --- a/go/vt/automation/copy_schema_shard_task.go +++ /dev/null @@ -1,51 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package automation - -import ( - "context" - - automationpb "vitess.io/vitess/go/vt/proto/automation" -) - -// CopySchemaShardTask runs vtctl CopySchemaShard to copy the schema from one shard to another. -type CopySchemaShardTask struct { -} - -// Run is part of the Task interface. -func (t *CopySchemaShardTask) Run(parameters map[string]string) ([]*automationpb.TaskContainer, string, error) { - args := []string{"CopySchemaShard"} - if tables := parameters["tables"]; tables != "" { - args = append(args, "--tables="+tables) - } - if excludeTables := parameters["exclude_tables"]; excludeTables != "" { - args = append(args, "--exclude_tables="+excludeTables) - } - args = append(args, parameters["source_keyspace_and_shard"], parameters["dest_keyspace_and_shard"]) - output, err := ExecuteVtctl(context.TODO(), parameters["vtctld_endpoint"], args) - return nil, output, err -} - -// RequiredParameters is part of the Task interface. -func (t *CopySchemaShardTask) RequiredParameters() []string { - return []string{"source_keyspace_and_shard", "dest_keyspace_and_shard", "vtctld_endpoint"} -} - -// OptionalParameters is part of the Task interface. -func (t *CopySchemaShardTask) OptionalParameters() []string { - return []string{"tables", "exclude_tables"} -} diff --git a/go/vt/automation/copy_schema_shard_task_test.go b/go/vt/automation/copy_schema_shard_task_test.go deleted file mode 100644 index 68d9110e860..00000000000 --- a/go/vt/automation/copy_schema_shard_task_test.go +++ /dev/null @@ -1,50 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package automation - -import ( - "flag" - "testing" - - "vitess.io/vitess/go/vt/vtctl/fakevtctlclient" - "vitess.io/vitess/go/vt/vtctl/vtctlclient" -) - -func TestCopySchemaShardTask(t *testing.T) { - fake := fakevtctlclient.NewFakeVtctlClient() - vtctlclient.RegisterFactory("fake", fake.FakeVtctlClientFactory) - defer vtctlclient.UnregisterFactoryForTest("fake") - flag.Set("vtctl_client_protocol", "fake") - fake.RegisterResult([]string{"CopySchemaShard", "test_keyspace/0", "test_keyspace/2"}, - "", // No output. - nil) // No error. - - task := &CopySchemaShardTask{} - parameters := map[string]string{ - "source_keyspace_and_shard": "test_keyspace/0", - "dest_keyspace_and_shard": "test_keyspace/2", - "vtctld_endpoint": "localhost:15000", - "exclude_tables": "", - } - testTask(t, "CopySchemaShard", task, parameters, fake) - - fake.RegisterResult([]string{"CopySchemaShard", "--exclude_tables=excluded_table1", "test_keyspace/0", "test_keyspace/2"}, - "", // No output. - nil) // No error. - parameters["exclude_tables"] = "excluded_table1" - testTask(t, "CopySchemaShard", task, parameters, fake) -} diff --git a/go/vt/automation/horizontal_resharding_task.go b/go/vt/automation/horizontal_resharding_task.go deleted file mode 100644 index 9df5e7c18ef..00000000000 --- a/go/vt/automation/horizontal_resharding_task.go +++ /dev/null @@ -1,125 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package automation - -import ( - "strings" - - automationpb "vitess.io/vitess/go/vt/proto/automation" - "vitess.io/vitess/go/vt/topo/topoproto" -) - -// HorizontalReshardingTask is a cluster operation which allows to increase the number of shards. -type HorizontalReshardingTask struct { -} - -// Run is part of the Task interface. -func (t *HorizontalReshardingTask) Run(parameters map[string]string) ([]*automationpb.TaskContainer, string, error) { - // Required parameters. - // Example: test_keyspace - keyspace := parameters["keyspace"] - // Example: 10-20 - sourceShards := strings.Split(parameters["source_shard_list"], ",") - // Example: 10-18,18-20 - destShards := strings.Split(parameters["dest_shard_list"], ",") - // Example: localhost:15000 - vtctldEndpoint := parameters["vtctld_endpoint"] - // Example: localhost:15001 - vtworkerEndpoint := parameters["vtworker_endpoint"] - - // Optional parameters. - // Example: unrelated1,unrelated2 - excludeTables := parameters["exclude_tables"] - // Example: 1 - minHealthyRdonlyTablets := parameters["min_healthy_rdonly_tablets"] - - var newTasks []*automationpb.TaskContainer - copySchemaTasks := NewTaskContainer() - for _, destShard := range destShards { - AddTask(copySchemaTasks, "CopySchemaShardTask", map[string]string{ - "source_keyspace_and_shard": topoproto.KeyspaceShardString(keyspace, sourceShards[0]), - "dest_keyspace_and_shard": topoproto.KeyspaceShardString(keyspace, destShard), - "exclude_tables": excludeTables, - "vtctld_endpoint": vtctldEndpoint, - }) - } - newTasks = append(newTasks, copySchemaTasks) - - splitCloneTasks := NewTaskContainer() - for _, sourceShard := range sourceShards { - // TODO(mberlin): Add a semaphore as argument to limit the parallism. - AddTask(splitCloneTasks, "SplitCloneTask", map[string]string{ - "keyspace": keyspace, - "source_shard": sourceShard, - "vtworker_endpoint": vtworkerEndpoint, - "exclude_tables": excludeTables, - "min_healthy_rdonly_tablets": minHealthyRdonlyTablets, - }) - } - newTasks = append(newTasks, splitCloneTasks) - - // TODO(mberlin): When the framework supports nesting tasks, these wait tasks should be run before each SplitDiff. - waitTasks := NewTaskContainer() - for _, destShard := range destShards { - AddTask(waitTasks, "WaitForFilteredReplicationTask", map[string]string{ - "keyspace": keyspace, - "shard": destShard, - "max_delay": "30s", - "vtctld_endpoint": vtctldEndpoint, - }) - } - newTasks = append(newTasks, waitTasks) - - // TODO(mberlin): Run all SplitDiffTasks in parallel which do not use overlapping source shards for the comparison. - for _, destShard := range destShards { - splitDiffTask := NewTaskContainer() - AddTask(splitDiffTask, "SplitDiffTask", map[string]string{ - "keyspace": keyspace, - "dest_shard": destShard, - "vtworker_endpoint": vtworkerEndpoint, - "exclude_tables": excludeTables, - "min_healthy_rdonly_tablets": minHealthyRdonlyTablets, - }) - newTasks = append(newTasks, splitDiffTask) - } - - for _, servedType := range []string{"rdonly", "replica", "primary"} { - migrateServedTypesTasks := NewTaskContainer() - for _, sourceShard := range sourceShards { - AddTask(migrateServedTypesTasks, "MigrateServedTypesTask", map[string]string{ - "keyspace": keyspace, - "source_shard": sourceShard, - "type": servedType, - "vtctld_endpoint": vtctldEndpoint, - }) - } - newTasks = append(newTasks, migrateServedTypesTasks) - } - - return newTasks, "", nil -} - -// RequiredParameters is part of the Task interface. -func (t *HorizontalReshardingTask) RequiredParameters() []string { - return []string{"keyspace", "source_shard_list", "dest_shard_list", - "vtctld_endpoint", "vtworker_endpoint"} -} - -// OptionalParameters is part of the Task interface. -func (t *HorizontalReshardingTask) OptionalParameters() []string { - return []string{"exclude_tables", "min_healthy_rdonly_tablets"} -} diff --git a/go/vt/automation/horizontal_resharding_task_test.go b/go/vt/automation/horizontal_resharding_task_test.go deleted file mode 100644 index 6a8eb44401f..00000000000 --- a/go/vt/automation/horizontal_resharding_task_test.go +++ /dev/null @@ -1,51 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package automation - -import ( - "testing" - - "google.golang.org/protobuf/encoding/prototext" -) - -func TestHorizontalReshardingTaskEmittedTasks(t *testing.T) { - reshardingTask := &HorizontalReshardingTask{} - - parameters := map[string]string{ - // Required parameters. - "keyspace": "test_keyspace", - "source_shard_list": "10-20", - "dest_shard_list": "10-18,18-20", - "vtctld_endpoint": "localhost:15000", - "vtworker_endpoint": "localhost:15001", - // Optional parameters. - "exclude_tables": "unrelated1,unrelated2", - "min_healthy_rdonly_tablets": "1", - } - - err := validateParameters(reshardingTask, parameters) - if err != nil { - t.Fatalf("Not all required parameters were specified: %v", err) - } - - newTaskContainers, _, _ := reshardingTask.Run(parameters) - - // TODO(mberlin): Check emitted tasks against expected output. - for _, tc := range newTaskContainers { - t.Logf("new tasks: %v", prototext.Format(tc)) - } -} diff --git a/go/vt/automation/id_generator.go b/go/vt/automation/id_generator.go deleted file mode 100644 index 558e8fab11b..00000000000 --- a/go/vt/automation/id_generator.go +++ /dev/null @@ -1,32 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package automation - -import ( - "strconv" - "sync/atomic" -) - -// IDGenerator generates unique task and cluster operation IDs. -type IDGenerator struct { - counter int64 -} - -// GetNextID returns an ID which wasn't returned before. -func (ig *IDGenerator) GetNextID() string { - return strconv.FormatInt(atomic.AddInt64(&ig.counter, 1), 10) -} diff --git a/go/vt/automation/migrate_served_from_task.go b/go/vt/automation/migrate_served_from_task.go deleted file mode 100644 index e6335a8f478..00000000000 --- a/go/vt/automation/migrate_served_from_task.go +++ /dev/null @@ -1,55 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package automation - -import ( - "context" - - automationpb "vitess.io/vitess/go/vt/proto/automation" - "vitess.io/vitess/go/vt/topo/topoproto" -) - -// MigrateServedFromTask runs vtctl MigrateServedFrom to let vertically split -// out tables get served from the new destination keyspace. -type MigrateServedFromTask struct { -} - -// Run is part of the Task interface. -func (t *MigrateServedFromTask) Run(parameters map[string]string) ([]*automationpb.TaskContainer, string, error) { - args := []string{"MigrateServedFrom"} - if cells := parameters["cells"]; cells != "" { - args = append(args, "--cells="+cells) - } - if reverse := parameters["reverse"]; reverse != "" { - args = append(args, "--reverse="+reverse) - } - args = append(args, - topoproto.KeyspaceShardString(parameters["dest_keyspace"], parameters["shard"]), - parameters["type"]) - output, err := ExecuteVtctl(context.TODO(), parameters["vtctld_endpoint"], args) - return nil, output, err -} - -// RequiredParameters is part of the Task interface. -func (t *MigrateServedFromTask) RequiredParameters() []string { - return []string{"dest_keyspace", "shard", "type", "vtctld_endpoint"} -} - -// OptionalParameters is part of the Task interface. -func (t *MigrateServedFromTask) OptionalParameters() []string { - return []string{"cells", "reverse"} -} diff --git a/go/vt/automation/migrate_served_types_task.go b/go/vt/automation/migrate_served_types_task.go deleted file mode 100644 index 79487629a87..00000000000 --- a/go/vt/automation/migrate_served_types_task.go +++ /dev/null @@ -1,55 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package automation - -import ( - "context" - - automationpb "vitess.io/vitess/go/vt/proto/automation" - "vitess.io/vitess/go/vt/topo/topoproto" -) - -// MigrateServedTypesTask runs vtctl MigrateServedTypes to migrate a serving -// type from the source shard to the shards that it replicates to. -type MigrateServedTypesTask struct { -} - -// Run is part of the Task interface. -func (t *MigrateServedTypesTask) Run(parameters map[string]string) ([]*automationpb.TaskContainer, string, error) { - args := []string{"MigrateServedTypes"} - if cells := parameters["cells"]; cells != "" { - args = append(args, "--cells="+cells) - } - if reverse := parameters["reverse"]; reverse != "" { - args = append(args, "--reverse="+reverse) - } - args = append(args, - topoproto.KeyspaceShardString(parameters["keyspace"], parameters["source_shard"]), - parameters["type"]) - output, err := ExecuteVtctl(context.TODO(), parameters["vtctld_endpoint"], args) - return nil, output, err -} - -// RequiredParameters is part of the Task interface. -func (t *MigrateServedTypesTask) RequiredParameters() []string { - return []string{"keyspace", "source_shard", "type", "vtctld_endpoint"} -} - -// OptionalParameters is part of the Task interface. -func (t *MigrateServedTypesTask) OptionalParameters() []string { - return []string{"cells", "reverse"} -} diff --git a/go/vt/automation/migrate_served_types_task_test.go b/go/vt/automation/migrate_served_types_task_test.go deleted file mode 100644 index 065f51bcf1c..00000000000 --- a/go/vt/automation/migrate_served_types_task_test.go +++ /dev/null @@ -1,51 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package automation - -import ( - "flag" - "testing" - - "vitess.io/vitess/go/vt/vtctl/fakevtctlclient" - "vitess.io/vitess/go/vt/vtctl/vtctlclient" -) - -func TestMigrateServedTypesTask(t *testing.T) { - fake := fakevtctlclient.NewFakeVtctlClient() - vtctlclient.RegisterFactory("fake", fake.FakeVtctlClientFactory) - defer vtctlclient.UnregisterFactoryForTest("fake") - flag.Set("vtctl_client_protocol", "fake") - task := &MigrateServedTypesTask{} - - fake.RegisterResult([]string{"MigrateServedTypes", "test_keyspace/0", "rdonly"}, - "", // No output. - nil) // No error. - parameters := map[string]string{ - "keyspace": "test_keyspace", - "source_shard": "0", - "type": "rdonly", - "vtctld_endpoint": "localhost:15000", - } - testTask(t, "MigrateServedTypes", task, parameters, fake) - - fake.RegisterResult([]string{"MigrateServedTypes", "--cells=cell1", "--reverse=true", "test_keyspace/0", "rdonly"}, - "", // No output. - nil) // No error. - parameters["cells"] = "cell1" - parameters["reverse"] = "true" - testTask(t, "MigrateServedTypes", task, parameters, fake) -} diff --git a/go/vt/automation/rebuild_keyspace_graph_task.go b/go/vt/automation/rebuild_keyspace_graph_task.go deleted file mode 100644 index 0604594e75c..00000000000 --- a/go/vt/automation/rebuild_keyspace_graph_task.go +++ /dev/null @@ -1,49 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package automation - -import ( - "context" - - automationpb "vitess.io/vitess/go/vt/proto/automation" -) - -// RebuildKeyspaceGraphTask runs vtctl RebuildKeyspaceGraph to migrate a serving -// type from the source shard to the shards that it replicates to. -type RebuildKeyspaceGraphTask struct { -} - -// Run is part of the Task interface. -func (t *RebuildKeyspaceGraphTask) Run(parameters map[string]string) ([]*automationpb.TaskContainer, string, error) { - args := []string{"RebuildKeyspaceGraph"} - if cells := parameters["cells"]; cells != "" { - args = append(args, "--cells="+cells) - } - args = append(args, parameters["keyspace"]) - output, err := ExecuteVtctl(context.TODO(), parameters["vtctld_endpoint"], args) - return nil, output, err -} - -// RequiredParameters is part of the Task interface. -func (t *RebuildKeyspaceGraphTask) RequiredParameters() []string { - return []string{"keyspace", "vtctld_endpoint"} -} - -// OptionalParameters is part of the Task interface. -func (t *RebuildKeyspaceGraphTask) OptionalParameters() []string { - return []string{"cells"} -} diff --git a/go/vt/automation/rebuild_keyspace_graph_task_test.go b/go/vt/automation/rebuild_keyspace_graph_task_test.go deleted file mode 100644 index 11db634a8d4..00000000000 --- a/go/vt/automation/rebuild_keyspace_graph_task_test.go +++ /dev/null @@ -1,48 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package automation - -import ( - "flag" - "testing" - - "vitess.io/vitess/go/vt/vtctl/fakevtctlclient" - "vitess.io/vitess/go/vt/vtctl/vtctlclient" -) - -func TestRebuildKeyspaceGraphTask(t *testing.T) { - fake := fakevtctlclient.NewFakeVtctlClient() - vtctlclient.RegisterFactory("fake", fake.FakeVtctlClientFactory) - defer vtctlclient.UnregisterFactoryForTest("fake") - flag.Set("vtctl_client_protocol", "fake") - task := &RebuildKeyspaceGraphTask{} - - fake.RegisterResult([]string{"RebuildKeyspaceGraph", "test_keyspace"}, - "", // No output. - nil) // No error. - parameters := map[string]string{ - "keyspace": "test_keyspace", - "vtctld_endpoint": "localhost:15000", - } - testTask(t, "RebuildKeyspaceGraph", task, parameters, fake) - - fake.RegisterResult([]string{"RebuildKeyspaceGraph", "--cells=cell1", "test_keyspace"}, - "", // No output. - nil) // No error. - parameters["cells"] = "cell1" - testTask(t, "RebuildKeyspaceGraph", task, parameters, fake) -} diff --git a/go/vt/automation/scheduler.go b/go/vt/automation/scheduler.go deleted file mode 100644 index 345f35ec341..00000000000 --- a/go/vt/automation/scheduler.go +++ /dev/null @@ -1,397 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -/* -Package automation contains code to execute high-level cluster operations -(e.g. resharding) as a series of low-level operations -(e.g. vtctl, shell commands, ...). -*/ -package automation - -import ( - "errors" - "fmt" - "sync" - - "vitess.io/vitess/go/vt/proto/automationservice" - - "context" - - "vitess.io/vitess/go/vt/log" - automationpb "vitess.io/vitess/go/vt/proto/automation" - "vitess.io/vitess/go/vt/vterrors" -) - -type schedulerState int32 - -const ( - stateNotRunning schedulerState = iota - stateRunning - stateShuttingDown - stateShutdown -) - -type taskCreator func(string) Task - -// Scheduler executes automation tasks and maintains the execution state. -type Scheduler struct { - automationservice.UnimplementedAutomationServer - - idGenerator IDGenerator - - mu sync.Mutex - // Guarded by "mu". - registeredClusterOperations map[string]bool - // Guarded by "mu". - toBeScheduledClusterOperations chan ClusterOperationInstance - // Guarded by "mu". - state schedulerState - - // Guarded by "taskCreatorMu". May be overridden by testing code. - taskCreator taskCreator - taskCreatorMu sync.Mutex - - pendingOpsWg *sync.WaitGroup - - muOpList sync.Mutex - // Guarded by "muOpList". - // The key of the map is ClusterOperationInstance.ID. - // This map contains a copy of the ClusterOperationInstance which is currently processed. - // The scheduler may update the copy with the latest status. - activeClusterOperations map[string]ClusterOperationInstance - // Guarded by "muOpList". - // The key of the map is ClusterOperationInstance.ID. - finishedClusterOperations map[string]ClusterOperationInstance -} - -// NewScheduler creates a new instance. -func NewScheduler() (*Scheduler, error) { - defaultClusterOperations := map[string]bool{ - "HorizontalReshardingTask": true, - "VerticalSplitTask": true, - } - - s := &Scheduler{ - registeredClusterOperations: defaultClusterOperations, - idGenerator: IDGenerator{}, - toBeScheduledClusterOperations: make(chan ClusterOperationInstance, 10), - state: stateNotRunning, - taskCreator: defaultTaskCreator, - pendingOpsWg: &sync.WaitGroup{}, - activeClusterOperations: make(map[string]ClusterOperationInstance), - finishedClusterOperations: make(map[string]ClusterOperationInstance), - } - - return s, nil -} - -func (s *Scheduler) registerClusterOperation(clusterOperationName string) { - s.mu.Lock() - defer s.mu.Unlock() - - s.registeredClusterOperations[clusterOperationName] = true -} - -// Run processes queued cluster operations. -func (s *Scheduler) Run() { - s.mu.Lock() - s.state = stateRunning - s.mu.Unlock() - - s.startProcessRequestsLoop() -} - -func (s *Scheduler) startProcessRequestsLoop() { - // Use a WaitGroup instead of just a done channel, because we want - // to be able to shut down the scheduler even if Run() was never executed. - s.pendingOpsWg.Add(1) - go s.processRequestsLoop() -} - -func (s *Scheduler) processRequestsLoop() { - defer s.pendingOpsWg.Done() - - for op := range s.toBeScheduledClusterOperations { - s.processClusterOperation(op) - } - log.Infof("Stopped processing loop for ClusterOperations.") -} - -func (s *Scheduler) processClusterOperation(clusterOp ClusterOperationInstance) { - if clusterOp.State == automationpb.ClusterOperationState_CLUSTER_OPERATION_DONE { - log.Infof("ClusterOperation: %v skipping because it is already done. Details: %v", clusterOp.Id, clusterOp) - return - } - - log.Infof("ClusterOperation: %v running. Details: %v", clusterOp.Id, clusterOp) - -clusterOpLoop: - for i := 0; i < len(clusterOp.SerialTasks); i++ { - taskContainer := clusterOp.SerialTasks[i] - for _, taskProto := range taskContainer.ParallelTasks { - newTaskContainers, output, err := s.runTask(taskProto, clusterOp.Id) - if err != nil { - MarkTaskFailed(taskProto, output, err) - clusterOp.Error = err.Error() - break clusterOpLoop - } else { - MarkTaskSucceeded(taskProto, output) - } - - if newTaskContainers != nil { - // Make sure all new tasks do not miss any required parameters. - err := s.validateTaskContainers(newTaskContainers) - if err != nil { - err = vterrors.Wrapf(err, "task: %v (%v/%v) emitted a new task which is not valid. Error: %v", taskProto.Name, clusterOp.Id, taskProto.Id, err) - log.Error(err) - MarkTaskFailed(taskProto, output, err) - clusterOp.Error = err.Error() - break clusterOpLoop - } - - clusterOp.InsertTaskContainers(newTaskContainers, i+1) - log.Infof("ClusterOperation: %v %d new task containers added by %v (%v/%v). Updated ClusterOperation: %v", - clusterOp.Id, len(newTaskContainers), taskProto.Name, clusterOp.Id, taskProto.Id, clusterOp) - } - s.Checkpoint(clusterOp) - } - } - - clusterOp.State = automationpb.ClusterOperationState_CLUSTER_OPERATION_DONE - log.Infof("ClusterOperation: %v finished. Details: %v", clusterOp.Id, clusterOp) - s.Checkpoint(clusterOp) - - // Move operation from active to finished. - s.muOpList.Lock() - defer s.muOpList.Unlock() - if _, ok := s.activeClusterOperations[clusterOp.Id]; !ok { - panic("Pending ClusterOperation was not recorded as active, but should have.") - } - delete(s.activeClusterOperations, clusterOp.Id) - s.finishedClusterOperations[clusterOp.Id] = clusterOp -} - -func (s *Scheduler) runTask(taskProto *automationpb.Task, clusterOpID string) ([]*automationpb.TaskContainer, string, error) { - if taskProto.State == automationpb.TaskState_DONE { - // Task is already done (e.g. because we resume from a checkpoint). - if taskProto.Error != "" { - log.Errorf("Task: %v (%v/%v) failed before. Aborting the ClusterOperation. Error: %v Details: %v", taskProto.Name, clusterOpID, taskProto.Id, taskProto.Error, taskProto) - return nil, "", errors.New(taskProto.Error) - } - log.Infof("Task: %v (%v/%v) skipped because it is already done. Full Details: %v", taskProto.Name, clusterOpID, taskProto.Id, taskProto) - return nil, taskProto.Output, nil - } - - task, err := s.createTaskInstance(taskProto.Name) - if err != nil { - log.Errorf("Task: %v (%v/%v) could not be instantiated. Error: %v Details: %v", taskProto.Name, clusterOpID, taskProto.Id, err, taskProto) - return nil, "", err - } - - taskProto.State = automationpb.TaskState_RUNNING - log.Infof("Task: %v (%v/%v) running. Details: %v", taskProto.Name, clusterOpID, taskProto.Id, taskProto) - newTaskContainers, output, err := task.Run(taskProto.Parameters) - log.Infof("Task: %v (%v/%v) finished. newTaskContainers: %v, output: %v, error: %v", taskProto.Name, clusterOpID, taskProto.Id, newTaskContainers, output, err) - - return newTaskContainers, output, err -} - -func (s *Scheduler) validateTaskContainers(newTaskContainers []*automationpb.TaskContainer) error { - for _, newTaskContainer := range newTaskContainers { - for _, newTaskProto := range newTaskContainer.ParallelTasks { - err := s.validateTaskSpecification(newTaskProto.Name, newTaskProto.Parameters) - if err != nil { - return fmt.Errorf("error: %v task: %v", err, newTaskProto) - } - } - } - return nil -} - -func defaultTaskCreator(taskName string) Task { - switch taskName { - case "HorizontalReshardingTask": - return &HorizontalReshardingTask{} - case "VerticalSplitTask": - return &VerticalSplitTask{} - case "CopySchemaShardTask": - return &CopySchemaShardTask{} - case "MigrateServedFromTask": - return &MigrateServedFromTask{} - case "MigrateServedTypesTask": - return &MigrateServedTypesTask{} - case "RebuildKeyspaceGraph": - return &RebuildKeyspaceGraphTask{} - case "SplitCloneTask": - return &SplitCloneTask{} - case "SplitDiffTask": - return &SplitDiffTask{} - case "VerticalSplitCloneTask": - return &VerticalSplitCloneTask{} - case "VerticalSplitDiffTask": - return &VerticalSplitDiffTask{} - case "WaitForFilteredReplicationTask": - return &WaitForFilteredReplicationTask{} - default: - return nil - } -} - -func (s *Scheduler) setTaskCreator(creator taskCreator) { - s.taskCreatorMu.Lock() - defer s.taskCreatorMu.Unlock() - - s.taskCreator = creator -} - -func (s *Scheduler) validateTaskSpecification(taskName string, parameters map[string]string) error { - taskInstanceForParametersCheck, err := s.createTaskInstance(taskName) - if err != nil { - return err - } - errParameters := validateParameters(taskInstanceForParametersCheck, parameters) - if errParameters != nil { - return errParameters - } - return nil -} - -func (s *Scheduler) createTaskInstance(taskName string) (Task, error) { - s.taskCreatorMu.Lock() - taskCreator := s.taskCreator - s.taskCreatorMu.Unlock() - - task := taskCreator(taskName) - if task == nil { - return nil, fmt.Errorf("no implementation found for: %v", taskName) - } - return task, nil -} - -// validateParameters returns an error if not all required parameters are provided in "parameters". -// Unknown parameters (neither required nor optional) result in an error. -func validateParameters(task Task, parameters map[string]string) error { - validParams := make(map[string]bool) - var missingParams []string - for _, reqParam := range task.RequiredParameters() { - if _, ok := parameters[reqParam]; ok { - validParams[reqParam] = true - } else { - missingParams = append(missingParams, reqParam) - } - } - if len(missingParams) > 0 { - return fmt.Errorf("required parameters are missing: %v", missingParams) - } - for _, optParam := range task.OptionalParameters() { - validParams[optParam] = true - } - for param := range parameters { - if !validParams[param] { - return fmt.Errorf("parameter %v is not allowed. Allowed required parameters: %v optional parameters: %v", - param, task.RequiredParameters(), task.OptionalParameters()) - } - } - return nil -} - -// EnqueueClusterOperation can be used to start a new cluster operation. -func (s *Scheduler) EnqueueClusterOperation(ctx context.Context, req *automationpb.EnqueueClusterOperationRequest) (*automationpb.EnqueueClusterOperationResponse, error) { - s.mu.Lock() - defer s.mu.Unlock() - - if s.state != stateRunning { - return nil, fmt.Errorf("scheduler is not running. State: %v", s.state) - } - - if !s.registeredClusterOperations[req.Name] { - return nil, fmt.Errorf("no ClusterOperation with name: %v is registered", req.Name) - } - - err := s.validateTaskSpecification(req.Name, req.Parameters) - if err != nil { - return nil, err - } - - clusterOpID := s.idGenerator.GetNextID() - taskIDGenerator := IDGenerator{} - initialTask := NewTaskContainerWithSingleTask(req.Name, req.Parameters) - clusterOp := NewClusterOperationInstance(clusterOpID, initialTask, &taskIDGenerator) - - s.muOpList.Lock() - s.activeClusterOperations[clusterOpID] = clusterOp.Clone() - s.muOpList.Unlock() - s.toBeScheduledClusterOperations <- clusterOp - - return &automationpb.EnqueueClusterOperationResponse{ - Id: clusterOp.Id, - }, nil -} - -// findClusterOp checks for a given ClusterOperation ID if it's in the list of active or finished operations. -func (s *Scheduler) findClusterOp(id string) (ClusterOperationInstance, error) { - var ok bool - var clusterOp ClusterOperationInstance - - s.muOpList.Lock() - defer s.muOpList.Unlock() - clusterOp, ok = s.activeClusterOperations[id] - if !ok { - clusterOp, ok = s.finishedClusterOperations[id] - } - if !ok { - return clusterOp, fmt.Errorf("ClusterOperation with id: %v not found", id) - } - return clusterOp.Clone(), nil -} - -// Checkpoint should be called every time the state of the cluster op changes. -// It is used to update the copy of the state in activeClusterOperations. -func (s *Scheduler) Checkpoint(clusterOp ClusterOperationInstance) { - // TODO(mberlin): Add here support for persistent checkpoints. - s.muOpList.Lock() - defer s.muOpList.Unlock() - s.activeClusterOperations[clusterOp.Id] = clusterOp.Clone() -} - -// GetClusterOperationDetails can be used to query the full details of active or finished operations. -func (s *Scheduler) GetClusterOperationDetails(ctx context.Context, req *automationpb.GetClusterOperationDetailsRequest) (*automationpb.GetClusterOperationDetailsResponse, error) { - clusterOp, err := s.findClusterOp(req.Id) - if err != nil { - return nil, err - } - return &automationpb.GetClusterOperationDetailsResponse{ - ClusterOp: clusterOp.ClusterOperation, - }, nil -} - -// ShutdownAndWait shuts down the scheduler and waits infinitely until all pending cluster operations have finished. -func (s *Scheduler) ShutdownAndWait() { - s.mu.Lock() - if s.state != stateShuttingDown { - s.state = stateShuttingDown - close(s.toBeScheduledClusterOperations) - } - s.mu.Unlock() - - log.Infof("Scheduler was shut down. Waiting for pending ClusterOperations to finish.") - s.pendingOpsWg.Wait() - - s.mu.Lock() - s.state = stateShutdown - s.mu.Unlock() - log.Infof("All pending ClusterOperations finished.") -} diff --git a/go/vt/automation/scheduler_test.go b/go/vt/automation/scheduler_test.go deleted file mode 100644 index d8cc881a6bd..00000000000 --- a/go/vt/automation/scheduler_test.go +++ /dev/null @@ -1,269 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package automation - -import ( - "strings" - "testing" - "time" - - "google.golang.org/protobuf/encoding/prototext" - - context "context" - - automationpb "vitess.io/vitess/go/vt/proto/automation" -) - -// newTestScheduler constructs a scheduler with test tasks. -// If tasks should be available as cluster operation, they still have to be registered manually with scheduler.registerClusterOperation. -func newTestScheduler(t *testing.T) *Scheduler { - scheduler, err := NewScheduler() - if err != nil { - t.Fatalf("Failed to create scheduler: %v", err) - } - scheduler.setTaskCreator(testingTaskCreator) - return scheduler -} - -func enqueueClusterOperationAndCheckOutput(t *testing.T, taskName string, expectedOutput string, expectedError string) *automationpb.ClusterOperation { - scheduler := newTestScheduler(t) - defer scheduler.ShutdownAndWait() - scheduler.registerClusterOperation("TestingEchoTask") - scheduler.registerClusterOperation("TestingFailTask") - scheduler.registerClusterOperation("TestingEmitEchoTask") - scheduler.registerClusterOperation("TestingEmitEchoFailEchoTask") - - scheduler.Run() - - enqueueRequest := &automationpb.EnqueueClusterOperationRequest{ - Name: taskName, - Parameters: map[string]string{ - "echo_text": expectedOutput, - }, - } - enqueueResponse, err := scheduler.EnqueueClusterOperation(context.Background(), enqueueRequest) - if err != nil { - t.Fatalf("Failed to start cluster operation. Request: %v Error: %v", enqueueRequest, err) - } - - return waitForClusterOperation(t, scheduler, enqueueResponse.Id, expectedOutput, expectedError) -} - -// waitForClusterOperation is a helper function which blocks until the Cluster Operation has finished. -func waitForClusterOperation(t *testing.T, scheduler *Scheduler, id string, expectedOutputLastTask string, expectedErrorLastTask string) *automationpb.ClusterOperation { - if expectedOutputLastTask == "" && expectedErrorLastTask == "" { - t.Fatal("Error in test: Cannot wait for an operation where both output and error are expected to be empty.") - } - - getDetailsRequest := &automationpb.GetClusterOperationDetailsRequest{ - Id: id, - } - - for { - getDetailsResponse, err := scheduler.GetClusterOperationDetails(context.Background(), getDetailsRequest) - if err != nil { - t.Fatalf("Failed to get details for cluster operation. Request: %v Error: %v", getDetailsRequest, err) - } - if getDetailsResponse.ClusterOp.State == automationpb.ClusterOperationState_CLUSTER_OPERATION_DONE { - tc := getDetailsResponse.ClusterOp.SerialTasks - // Check the last task which have finished. (It may not be the last one because tasks can fail.) - var lastTc *automationpb.TaskContainer - for i := len(tc) - 1; i >= 0; i-- { - if tc[i].ParallelTasks[len(tc[i].ParallelTasks)-1].State == automationpb.TaskState_DONE { - lastTc = tc[i] - break - } - } - if expectedOutputLastTask != "" { - if got := lastTc.ParallelTasks[len(lastTc.ParallelTasks)-1].Output; !strings.Contains(got, expectedOutputLastTask) { - protoText, _ := prototext.Marshal(getDetailsResponse.ClusterOp) - t.Fatalf("ClusterOperation finished but did not contain expected output. got: %v want: %v Full ClusterOperation details: %s", got, expectedOutputLastTask, protoText) - } - } - if expectedErrorLastTask != "" { - if lastError := lastTc.ParallelTasks[len(lastTc.ParallelTasks)-1].Error; !strings.Contains(lastError, expectedErrorLastTask) { - t.Fatalf("ClusterOperation finished last error does not contain expected error. got: '%v' want: '%v' Full ClusterOperation details: %v", lastError, expectedErrorLastTask, getDetailsResponse.ClusterOp) - } - } - return getDetailsResponse.ClusterOp - } - - t.Logf("Waiting for clusterOp: %v", getDetailsResponse.ClusterOp) - time.Sleep(5 * time.Millisecond) - } -} - -func TestSchedulerImmediateShutdown(t *testing.T) { - // Make sure that the scheduler shuts down cleanly when it was instantiated, but not started with Run(). - scheduler, err := NewScheduler() - if err != nil { - t.Fatalf("Failed to create scheduler: %v", err) - } - scheduler.ShutdownAndWait() -} - -func TestEnqueueSingleTask(t *testing.T) { - enqueueClusterOperationAndCheckOutput(t, "TestingEchoTask", "echoed text", "") -} - -func TestEnqueueEmittingTask(t *testing.T) { - enqueueClusterOperationAndCheckOutput(t, "TestingEmitEchoTask", "echoed text from emitted task", "") -} - -func TestFailedTaskFailsClusterOperation(t *testing.T) { - enqueueClusterOperationAndCheckOutput(t, "TestingFailTask", "something went wrong", "full error message") -} - -func TestFailedTaskFailsWholeClusterOperationEarly(t *testing.T) { - // If a task fails in the middle of a cluster operation, the remaining tasks must not be executed. - details := enqueueClusterOperationAndCheckOutput(t, "TestingEmitEchoFailEchoTask", "", "full error message") - got := details.SerialTasks[2].ParallelTasks[0].Error - want := "full error message" - if got != want { - t.Errorf("TestFailedTaskFailsWholeClusterOperationEarly: got error: '%v' want error: '%v'", got, want) - } - if details.SerialTasks[3].ParallelTasks[0].State != automationpb.TaskState_NOT_STARTED { - t.Errorf("TestFailedTaskFailsWholeClusterOperationEarly: Task after a failing task must not have been started.") - } -} - -func TestEnqueueFailsDueToMissingParameter(t *testing.T) { - scheduler := newTestScheduler(t) - defer scheduler.ShutdownAndWait() - scheduler.registerClusterOperation("TestingEchoTask") - - scheduler.Run() - - enqueueRequest := &automationpb.EnqueueClusterOperationRequest{ - Name: "TestingEchoTask", - Parameters: map[string]string{ - "unrelevant-parameter": "value", - }, - } - enqueueResponse, err := scheduler.EnqueueClusterOperation(context.Background(), enqueueRequest) - - if err == nil { - t.Fatalf("Scheduler should have failed to start cluster operation because not all required parameters were provided. Request: %v Error: %v Response: %v", enqueueRequest, err, enqueueResponse) - } - want := "required parameters are missing: [echo_text]" - if err.Error() != want { - t.Fatalf("Wrong error message. got: '%v' want: '%v'", err, want) - } -} - -func TestEnqueueFailsDueToUnregisteredClusterOperation(t *testing.T) { - scheduler := newTestScheduler(t) - defer scheduler.ShutdownAndWait() - - scheduler.Run() - - enqueueRequest := &automationpb.EnqueueClusterOperationRequest{ - Name: "TestingEchoTask", - Parameters: map[string]string{ - "unrelevant-parameter": "value", - }, - } - enqueueResponse, err := scheduler.EnqueueClusterOperation(context.Background(), enqueueRequest) - - if err == nil { - t.Fatalf("Scheduler should have failed to start cluster operation because it should not have been registered. Request: %v Error: %v Response: %v", enqueueRequest, err, enqueueResponse) - } - want := "no ClusterOperation with name: TestingEchoTask is registered" - if err.Error() != want { - t.Fatalf("Wrong error message. got: '%v' want: '%v'", err, want) - } -} - -func TestGetDetailsFailsUnknownId(t *testing.T) { - scheduler := newTestScheduler(t) - defer scheduler.ShutdownAndWait() - - scheduler.Run() - - getDetailsRequest := &automationpb.GetClusterOperationDetailsRequest{ - Id: "-1", // There will never be a ClusterOperation with this id. - } - - getDetailsResponse, err := scheduler.GetClusterOperationDetails(context.Background(), getDetailsRequest) - if err == nil { - t.Fatalf("Did not fail to get details for invalid ClusterOperation id. Request: %v Response: %v Error: %v", getDetailsRequest, getDetailsResponse, err) - } - want := "ClusterOperation with id: -1 not found" - if err.Error() != want { - t.Fatalf("Wrong error message. got: '%v' want: '%v'", err, want) - } -} - -func TestEnqueueFailsBecauseTaskInstanceCannotBeCreated(t *testing.T) { - scheduler := newTestScheduler(t) - defer scheduler.ShutdownAndWait() - scheduler.setTaskCreator(defaultTaskCreator) - // TestingEchoTask is registered as cluster operation, but its task cannot be instantied because "testingTaskCreator" was not set. - scheduler.registerClusterOperation("TestingEchoTask") - - scheduler.Run() - - enqueueRequest := &automationpb.EnqueueClusterOperationRequest{ - Name: "TestingEchoTask", - Parameters: map[string]string{ - "unrelevant-parameter": "value", - }, - } - enqueueResponse, err := scheduler.EnqueueClusterOperation(context.Background(), enqueueRequest) - - if err == nil { - t.Fatalf("Scheduler should have failed to start cluster operation because the task could not be instantiated. Request: %v Error: %v Response: %v", enqueueRequest, err, enqueueResponse) - } - want := "no implementation found for: TestingEchoTask" - if err.Error() != want { - t.Fatalf("Wrong error message. got: '%v' want: '%v'", err, want) - } -} - -func TestTaskEmitsTaskWhichCannotBeInstantiated(t *testing.T) { - scheduler := newTestScheduler(t) - defer scheduler.ShutdownAndWait() - scheduler.setTaskCreator(func(taskName string) Task { - // TaskCreator which doesn't know TestingEchoTask (but emitted by TestingEmitEchoTask). - switch taskName { - case "TestingEmitEchoTask": - return &TestingEmitEchoTask{} - default: - return nil - } - }) - scheduler.registerClusterOperation("TestingEmitEchoTask") - - scheduler.Run() - - enqueueRequest := &automationpb.EnqueueClusterOperationRequest{ - Name: "TestingEmitEchoTask", - Parameters: map[string]string{ - "echo_text": "to be emitted task should fail to instantiate", - }, - } - enqueueResponse, err := scheduler.EnqueueClusterOperation(context.Background(), enqueueRequest) - if err != nil { - t.Fatalf("Failed to start cluster operation. Request: %v Error: %v", enqueueRequest, err) - } - - details := waitForClusterOperation(t, scheduler, enqueueResponse.Id, "emitted TestingEchoTask", "no implementation found for: TestingEchoTask") - if len(details.SerialTasks) != 1 { - protoText, _ := prototext.Marshal(details) - t.Errorf("A task has been emitted, but it shouldn't. Details:\n%s", protoText) - } -} diff --git a/go/vt/automation/split_clone_task.go b/go/vt/automation/split_clone_task.go deleted file mode 100644 index 703bd2d1b90..00000000000 --- a/go/vt/automation/split_clone_task.go +++ /dev/null @@ -1,85 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package automation - -import ( - "context" - - automationpb "vitess.io/vitess/go/vt/proto/automation" - "vitess.io/vitess/go/vt/topo/topoproto" -) - -// SplitCloneTask runs SplitClone on a remote vtworker to split an existing shard. -type SplitCloneTask struct { -} - -// Run is part of the Task interface. -func (t *SplitCloneTask) Run(parameters map[string]string) ([]*automationpb.TaskContainer, string, error) { - // Run a "Reset" first to clear the state of a previous finished command. - // This reset is best effort. We ignore the output and error of it. - // TODO(mberlin): Remove explicit reset when vtworker supports it implicility. - ExecuteVtworker(context.TODO(), parameters["vtworker_endpoint"], []string{"Reset"}) - - // TODO(mberlin): Add parameters for the following options? - // '--source_reader_count', '1', - // '--destination_writer_count', '1', - args := []string{"SplitClone"} - if online := parameters["online"]; online != "" { - args = append(args, "--online="+online) - } - if offline := parameters["offline"]; offline != "" { - args = append(args, "--offline="+offline) - } - if excludeTables := parameters["exclude_tables"]; excludeTables != "" { - args = append(args, "--exclude_tables="+excludeTables) - } - if chunkCount := parameters["chunk_count"]; chunkCount != "" { - args = append(args, "--chunk_count="+chunkCount) - } - if minRowsPerChunk := parameters["min_rows_per_chunk"]; minRowsPerChunk != "" { - args = append(args, "--min_rows_per_chunk="+minRowsPerChunk) - } - if writeQueryMaxRows := parameters["write_query_max_rows"]; writeQueryMaxRows != "" { - args = append(args, "--write_query_max_rows="+writeQueryMaxRows) - } - if writeQueryMaxSize := parameters["write_query_max_size"]; writeQueryMaxSize != "" { - args = append(args, "--write_query_max_size="+writeQueryMaxSize) - } - if minHealthyRdonlyTablets := parameters["min_healthy_rdonly_tablets"]; minHealthyRdonlyTablets != "" { - args = append(args, "--min_healthy_rdonly_tablets="+minHealthyRdonlyTablets) - } - if maxTPS := parameters["max_tps"]; maxTPS != "" { - args = append(args, "--max_tps="+maxTPS) - } - if maxReplicationLag := parameters["max_replication_lag"]; maxReplicationLag != "" { - args = append(args, "--max_replication_lag="+maxReplicationLag) - } - args = append(args, topoproto.KeyspaceShardString(parameters["keyspace"], parameters["source_shard"])) - - output, err := ExecuteVtworker(context.TODO(), parameters["vtworker_endpoint"], args) - return nil, output, err -} - -// RequiredParameters is part of the Task interface. -func (t *SplitCloneTask) RequiredParameters() []string { - return []string{"keyspace", "source_shard", "vtworker_endpoint"} -} - -// OptionalParameters is part of the Task interface. -func (t *SplitCloneTask) OptionalParameters() []string { - return []string{"online", "offline", "exclude_tables", "chunk_count", "min_rows_per_chunk", "write_query_max_rows", "write_query_max_size", "min_healthy_rdonly_tablets", "max_tps", "max_replication_lag"} -} diff --git a/go/vt/automation/split_clone_task_test.go b/go/vt/automation/split_clone_task_test.go deleted file mode 100644 index 5184f8f7aec..00000000000 --- a/go/vt/automation/split_clone_task_test.go +++ /dev/null @@ -1,65 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package automation - -import ( - "flag" - "testing" - - "vitess.io/vitess/go/vt/worker/fakevtworkerclient" - "vitess.io/vitess/go/vt/worker/vtworkerclient" -) - -func TestSplitCloneTask(t *testing.T) { - fake := fakevtworkerclient.NewFakeVtworkerClient() - vtworkerclient.RegisterFactory("fake", fake.FakeVtworkerClientFactory) - defer vtworkerclient.UnregisterFactoryForTest("fake") - flag.Set("vtworker_client_protocol", "fake") - fake.RegisterResult([]string{"SplitClone", "--online=false", "--offline=true", "--exclude_tables=unrelated1", "--chunk_count=2", "--min_rows_per_chunk=4", "--write_query_max_rows=1", "--write_query_max_size=1024", "--min_healthy_rdonly_tablets=1", "--max_tps=100", "--max_replication_lag=5", "test_keyspace/0"}, - "", // No output. - nil) // No error. - - task := &SplitCloneTask{} - parameters := map[string]string{ - "keyspace": "test_keyspace", - "source_shard": "0", - "vtworker_endpoint": "localhost:15001", - "online": "false", - "offline": "true", - "exclude_tables": "unrelated1", - "chunk_count": "2", - "min_rows_per_chunk": "4", - "write_query_max_rows": "1", - "write_query_max_size": "1024", - "min_healthy_rdonly_tablets": "1", - "max_tps": "100", - "max_replication_lag": "5", - } - - err := validateParameters(task, parameters) - if err != nil { - t.Fatalf("Not all required parameters were specified: %v", err) - } - - newTasks, _ /* output */, err := task.Run(parameters) - if newTasks != nil { - t.Errorf("Task should not emit new tasks: %v", newTasks) - } - if err != nil { - t.Errorf("Task should not fail: %v", err) - } -} diff --git a/go/vt/automation/split_diff_task.go b/go/vt/automation/split_diff_task.go deleted file mode 100644 index 7f3cf906d7f..00000000000 --- a/go/vt/automation/split_diff_task.go +++ /dev/null @@ -1,58 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package automation - -import ( - "context" - - automationpb "vitess.io/vitess/go/vt/proto/automation" - "vitess.io/vitess/go/vt/topo/topoproto" -) - -// SplitDiffTask runs SplitDiff on a remote vtworker to compare the old shard against its new split shards. -type SplitDiffTask struct { -} - -// Run is part of the Task interface. -func (t *SplitDiffTask) Run(parameters map[string]string) ([]*automationpb.TaskContainer, string, error) { - // Run a "Reset" first to clear the state of a previous finished command. - // This reset is best effort. We ignore the output and error of it. - // TODO(mberlin): Remove explicit reset when vtworker supports it implicility. - ExecuteVtworker(context.TODO(), parameters["vtworker_endpoint"], []string{"Reset"}) - - args := []string{"SplitDiff"} - if excludeTables := parameters["exclude_tables"]; excludeTables != "" { - args = append(args, "--exclude_tables="+excludeTables) - } - if minHealthyRdonlyTablets := parameters["min_healthy_rdonly_tablets"]; minHealthyRdonlyTablets != "" { - args = append(args, "--min_healthy_rdonly_tablets="+minHealthyRdonlyTablets) - } - args = append(args, topoproto.KeyspaceShardString(parameters["keyspace"], parameters["dest_shard"])) - - output, err := ExecuteVtworker(context.TODO(), parameters["vtworker_endpoint"], args) - return nil, output, err -} - -// RequiredParameters is part of the Task interface. -func (t *SplitDiffTask) RequiredParameters() []string { - return []string{"keyspace", "dest_shard", "vtworker_endpoint"} -} - -// OptionalParameters is part of the Task interface. -func (t *SplitDiffTask) OptionalParameters() []string { - return []string{"exclude_tables", "min_healthy_rdonly_tablets"} -} diff --git a/go/vt/automation/task.go b/go/vt/automation/task.go deleted file mode 100644 index 0966a1b8bac..00000000000 --- a/go/vt/automation/task.go +++ /dev/null @@ -1,35 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package automation - -import ( - automationpb "vitess.io/vitess/go/vt/proto/automation" -) - -// Task implementations can be executed by the scheduler. -type Task interface { - // Run executes the task using the key/values from parameters. - // "newTaskContainers" contains new tasks which the task can emit. They'll be inserted in the cluster operation directly after this task. It may be "nil". - // "output" may be empty. It contains any text which maybe must e.g. to debug the task or show it in the UI. - Run(parameters map[string]string) (newTaskContainers []*automationpb.TaskContainer, output string, err error) - - // RequiredParameters() returns a list of parameter keys which must be provided as input for Run(). - RequiredParameters() []string - - // OptionalParameters() returns a list of parameter keys which are optional input for Run(). - OptionalParameters() []string -} diff --git a/go/vt/automation/task_containers.go b/go/vt/automation/task_containers.go deleted file mode 100644 index e5986f414b6..00000000000 --- a/go/vt/automation/task_containers.go +++ /dev/null @@ -1,55 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package automation - -import ( - automationpb "vitess.io/vitess/go/vt/proto/automation" -) - -// Helper functions for "TaskContainer" protobuf message. - -// NewTaskContainerWithSingleTask creates a new task container with exactly one task. -func NewTaskContainerWithSingleTask(taskName string, parameters map[string]string) *automationpb.TaskContainer { - return &automationpb.TaskContainer{ - ParallelTasks: []*automationpb.Task{ - NewTask(taskName, parameters), - }, - } -} - -// NewTaskContainer creates an empty task container. Use AddTask() to add tasks to it. -func NewTaskContainer() *automationpb.TaskContainer { - return &automationpb.TaskContainer{ - ParallelTasks: []*automationpb.Task{}, - } -} - -// AddTask adds a new task to an existing task container. -func AddTask(t *automationpb.TaskContainer, taskName string, parameters map[string]string) { - t.ParallelTasks = append(t.ParallelTasks, NewTask(taskName, parameters)) -} - -// AddMissingTaskID assigns a task id to each task in "tc". -func AddMissingTaskID(tc []*automationpb.TaskContainer, taskIDGenerator *IDGenerator) { - for _, taskContainer := range tc { - for _, task := range taskContainer.ParallelTasks { - if task.Id == "" { - task.Id = taskIDGenerator.GetNextID() - } - } - } -} diff --git a/go/vt/automation/tasks.go b/go/vt/automation/tasks.go deleted file mode 100644 index 00e4bb99422..00000000000 --- a/go/vt/automation/tasks.go +++ /dev/null @@ -1,45 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package automation - -import ( - automationpb "vitess.io/vitess/go/vt/proto/automation" -) - -// Helper functions for "Task" protobuf message. - -// MarkTaskSucceeded marks the task as done. -func MarkTaskSucceeded(t *automationpb.Task, output string) { - t.State = automationpb.TaskState_DONE - t.Output = output -} - -// MarkTaskFailed marks the task as failed. -func MarkTaskFailed(t *automationpb.Task, output string, err error) { - t.State = automationpb.TaskState_DONE - t.Output = output - t.Error = err.Error() -} - -// NewTask creates a new task protobuf message for "taskName" with "parameters". -func NewTask(taskName string, parameters map[string]string) *automationpb.Task { - return &automationpb.Task{ - State: automationpb.TaskState_NOT_STARTED, - Name: taskName, - Parameters: parameters, - } -} diff --git a/go/vt/automation/testutils_test.go b/go/vt/automation/testutils_test.go deleted file mode 100644 index da6026d7423..00000000000 --- a/go/vt/automation/testutils_test.go +++ /dev/null @@ -1,138 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package automation - -import ( - "errors" - "testing" - - automationpb "vitess.io/vitess/go/vt/proto/automation" - "vitess.io/vitess/go/vt/vtctl/fakevtctlclient" -) - -func testingTaskCreator(taskName string) Task { - switch taskName { - // Tasks for testing only. - case "TestingEchoTask": - return &TestingEchoTask{} - case "TestingFailTask": - return &TestingFailTask{} - case "TestingEmitEchoTask": - return &TestingEmitEchoTask{} - case "TestingEmitEchoFailEchoTask": - return &TestingEmitEchoFailEchoTask{} - default: - return nil - } -} - -// TestingEchoTask is used only for testing. It returns the join of all parameter values. -type TestingEchoTask struct { -} - -func (t *TestingEchoTask) Run(parameters map[string]string) (newTasks []*automationpb.TaskContainer, output string, err error) { - for _, v := range parameters { - output += v - } - return -} - -func (t *TestingEchoTask) RequiredParameters() []string { - return []string{"echo_text"} -} - -func (t *TestingEchoTask) OptionalParameters() []string { - return nil -} - -// TestingFailTask is used only for testing. It always fails. -type TestingFailTask struct { -} - -func (t *TestingFailTask) Run(parameters map[string]string) (newTasks []*automationpb.TaskContainer, output string, err error) { - return nil, "something went wrong", errors.New("full error message") -} - -func (t *TestingFailTask) RequiredParameters() []string { - return []string{"echo_text"} -} - -func (t *TestingFailTask) OptionalParameters() []string { - return nil -} - -// TestingEmitEchoTask is used only for testing. It emits a TestingEchoTask. -type TestingEmitEchoTask struct { -} - -func (t *TestingEmitEchoTask) Run(parameters map[string]string) (newTasks []*automationpb.TaskContainer, output string, err error) { - return []*automationpb.TaskContainer{ - NewTaskContainerWithSingleTask("TestingEchoTask", parameters), - }, "emitted TestingEchoTask", nil -} - -func (t *TestingEmitEchoTask) RequiredParameters() []string { - return []string{"echo_text"} -} - -func (t *TestingEmitEchoTask) OptionalParameters() []string { - return nil -} - -// TestingEmitEchoFailEchoTask is used only for testing. -// It emits three sequential tasks: Echo, Fail, Echo. -type TestingEmitEchoFailEchoTask struct { -} - -func (t *TestingEmitEchoFailEchoTask) Run(parameters map[string]string) (newTasks []*automationpb.TaskContainer, output string, err error) { - newTasks = []*automationpb.TaskContainer{ - NewTaskContainerWithSingleTask("TestingEchoTask", parameters), - NewTaskContainerWithSingleTask("TestingFailTask", parameters), - NewTaskContainerWithSingleTask("TestingEchoTask", parameters), - } - return newTasks, "emitted tasks: Echo, Fail, Echo", nil -} - -func (t *TestingEmitEchoFailEchoTask) RequiredParameters() []string { - return []string{"echo_text"} -} - -func (t *TestingEmitEchoFailEchoTask) OptionalParameters() []string { - return nil -} - -// testTask runs the given tasks and checks if it succeeds. -// To make the task succeed you have to register the result with a fake first -// e.g. see migrate_served_types_task_test.go for an example. -func testTask(t *testing.T, test string, task Task, parameters map[string]string, vtctlClientFake *fakevtctlclient.FakeVtctlClient) { - err := validateParameters(task, parameters) - if err != nil { - t.Fatalf("%s: Not all required parameters were specified: %v", test, err) - } - - newTasks, _ /* output */, err := task.Run(parameters) - if newTasks != nil { - t.Errorf("%s: Task should not emit new tasks: %v", test, newTasks) - } - if err != nil { - t.Errorf("%s: Task should not fail: %v", err, test) - } - - if c := vtctlClientFake.RegisteredCommands(); len(c) != 0 { - t.Errorf("Not all registered results were consumed from the fake. Commands left: %v", c) - } -} diff --git a/go/vt/automation/vertical_split_clone_task.go b/go/vt/automation/vertical_split_clone_task.go deleted file mode 100644 index f3ee74dffb9..00000000000 --- a/go/vt/automation/vertical_split_clone_task.go +++ /dev/null @@ -1,84 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package automation - -import ( - "context" - - automationpb "vitess.io/vitess/go/vt/proto/automation" - "vitess.io/vitess/go/vt/topo/topoproto" -) - -// VerticalSplitCloneTask runs VerticalSplitClone on a remote vtworker to -// split out tables from an existing keyspace to a different keyspace. -type VerticalSplitCloneTask struct { -} - -// Run is part of the Task interface. -func (t *VerticalSplitCloneTask) Run(parameters map[string]string) ([]*automationpb.TaskContainer, string, error) { - // Run a "Reset" first to clear the state of a previous finished command. - // This reset is best effort. We ignore the output and error of it. - // TODO(mberlin): Remove explicit reset when vtworker supports it implicility. - ExecuteVtworker(context.TODO(), parameters["vtworker_endpoint"], []string{"Reset"}) - - // TODO(mberlin): Add parameters for the following options? - // '--source_reader_count', '1', - // '--destination_writer_count', '1', - args := []string{"VerticalSplitClone"} - args = append(args, "--tables="+parameters["tables"]) - if online := parameters["online"]; online != "" { - args = append(args, "--online="+online) - } - if offline := parameters["offline"]; offline != "" { - args = append(args, "--offline="+offline) - } - if chunkCount := parameters["chunk_count"]; chunkCount != "" { - args = append(args, "--chunk_count="+chunkCount) - } - if minRowsPerChunk := parameters["min_rows_per_chunk"]; minRowsPerChunk != "" { - args = append(args, "--min_rows_per_chunk="+minRowsPerChunk) - } - if writeQueryMaxRows := parameters["write_query_max_rows"]; writeQueryMaxRows != "" { - args = append(args, "--write_query_max_rows="+writeQueryMaxRows) - } - if writeQueryMaxSize := parameters["write_query_max_size"]; writeQueryMaxSize != "" { - args = append(args, "--write_query_max_size="+writeQueryMaxSize) - } - if minHealthyRdonlyTablets := parameters["min_healthy_rdonly_tablets"]; minHealthyRdonlyTablets != "" { - args = append(args, "--min_healthy_rdonly_tablets="+minHealthyRdonlyTablets) - } - if maxTPS := parameters["max_tps"]; maxTPS != "" { - args = append(args, "--max_tps="+maxTPS) - } - if maxReplicationLag := parameters["max_replication_lag"]; maxReplicationLag != "" { - args = append(args, "--max_replication_lag="+maxReplicationLag) - } - args = append(args, topoproto.KeyspaceShardString(parameters["dest_keyspace"], parameters["shard"])) - - output, err := ExecuteVtworker(context.TODO(), parameters["vtworker_endpoint"], args) - return nil, output, err -} - -// RequiredParameters is part of the Task interface. -func (t *VerticalSplitCloneTask) RequiredParameters() []string { - return []string{"dest_keyspace", "shard", "tables", "vtworker_endpoint"} -} - -// OptionalParameters is part of the Task interface. -func (t *VerticalSplitCloneTask) OptionalParameters() []string { - return []string{"online", "offline", "chunk_count", "min_rows_per_chunk", "write_query_max_rows", "write_query_max_size", "min_healthy_rdonly_tablets", "max_tps", "max_replication_lag"} -} diff --git a/go/vt/automation/vertical_split_clone_task_test.go b/go/vt/automation/vertical_split_clone_task_test.go deleted file mode 100644 index 6b9fef21c4a..00000000000 --- a/go/vt/automation/vertical_split_clone_task_test.go +++ /dev/null @@ -1,65 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package automation - -import ( - "flag" - "testing" - - "vitess.io/vitess/go/vt/worker/fakevtworkerclient" - "vitess.io/vitess/go/vt/worker/vtworkerclient" -) - -func TestVerticalSplitCloneTask(t *testing.T) { - fake := fakevtworkerclient.NewFakeVtworkerClient() - vtworkerclient.RegisterFactory("fake", fake.FakeVtworkerClientFactory) - defer vtworkerclient.UnregisterFactoryForTest("fake") - flag.Set("vtworker_client_protocol", "fake") - fake.RegisterResult([]string{"VerticalSplitClone", "--tables=moving1", "--online=false", "--offline=true", "--chunk_count=2", "--min_rows_per_chunk=4", "--write_query_max_rows=1", "--write_query_max_size=1024", "--min_healthy_rdonly_tablets=1", "--max_tps=100", "--max_replication_lag=5", "dest_keyspace/0"}, - "", // No output. - nil) // No error. - - task := &VerticalSplitCloneTask{} - parameters := map[string]string{ - "dest_keyspace": "dest_keyspace", - "shard": "0", - "tables": "moving1", - "vtworker_endpoint": "localhost:15001", - "online": "false", - "offline": "true", - "chunk_count": "2", - "min_rows_per_chunk": "4", - "write_query_max_rows": "1", - "write_query_max_size": "1024", - "min_healthy_rdonly_tablets": "1", - "max_tps": "100", - "max_replication_lag": "5", - } - - err := validateParameters(task, parameters) - if err != nil { - t.Fatalf("Not all required parameters were specified: %v", err) - } - - newTasks, _ /* output */, err := task.Run(parameters) - if newTasks != nil { - t.Errorf("Task should not emit new tasks: %v", newTasks) - } - if err != nil { - t.Errorf("Task should not fail: %v", err) - } -} diff --git a/go/vt/automation/vertical_split_diff_task.go b/go/vt/automation/vertical_split_diff_task.go deleted file mode 100644 index c53b0270455..00000000000 --- a/go/vt/automation/vertical_split_diff_task.go +++ /dev/null @@ -1,56 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package automation - -import ( - "context" - - automationpb "vitess.io/vitess/go/vt/proto/automation" - "vitess.io/vitess/go/vt/topo/topoproto" -) - -// VerticalSplitDiffTask runs VerticalSplitDiff on a remote vtworker to compare -// the split out tables against the source keyspace. -type VerticalSplitDiffTask struct { -} - -// Run is part of the Task interface. -func (t *VerticalSplitDiffTask) Run(parameters map[string]string) ([]*automationpb.TaskContainer, string, error) { - // Run a "Reset" first to clear the state of a previous finished command. - // This reset is best effort. We ignore the output and error of it. - // TODO(mberlin): Remove explicit reset when vtworker supports it implicility. - ExecuteVtworker(context.TODO(), parameters["vtworker_endpoint"], []string{"Reset"}) - - args := []string{"VerticalSplitDiff"} - if minHealthyRdonlyTablets := parameters["min_healthy_rdonly_tablets"]; minHealthyRdonlyTablets != "" { - args = append(args, "--min_healthy_rdonly_tablets="+minHealthyRdonlyTablets) - } - args = append(args, topoproto.KeyspaceShardString(parameters["dest_keyspace"], parameters["shard"])) - - output, err := ExecuteVtworker(context.TODO(), parameters["vtworker_endpoint"], args) - return nil, output, err -} - -// RequiredParameters is part of the Task interface. -func (t *VerticalSplitDiffTask) RequiredParameters() []string { - return []string{"dest_keyspace", "shard", "vtworker_endpoint"} -} - -// OptionalParameters is part of the Task interface. -func (t *VerticalSplitDiffTask) OptionalParameters() []string { - return []string{"min_healthy_rdonly_tablets"} -} diff --git a/go/vt/automation/vertical_split_task.go b/go/vt/automation/vertical_split_task.go deleted file mode 100644 index 0f0b3faab44..00000000000 --- a/go/vt/automation/vertical_split_task.go +++ /dev/null @@ -1,125 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package automation - -import ( - "strings" - - automationpb "vitess.io/vitess/go/vt/proto/automation" - "vitess.io/vitess/go/vt/topo/topoproto" -) - -// VerticalSplitTask is a cluster operation to split out specific tables of one -// keyspace to a different keyspace. -type VerticalSplitTask struct { -} - -// Run is part of the Task interface. -func (t *VerticalSplitTask) Run(parameters map[string]string) ([]*automationpb.TaskContainer, string, error) { - // Required parameters. - // Example: source_keyspace - sourceKeyspace := parameters["source_keyspace"] - // Example: destination_keyspace - destKeyspace := parameters["dest_keyspace"] - // Example: 10-20 - shards := strings.Split(parameters["shard_list"], ",") - // Example: table1,table2 - tables := parameters["tables"] - // Example: localhost:15000 - vtctldEndpoint := parameters["vtctld_endpoint"] - // Example: localhost:15001 - vtworkerEndpoint := parameters["vtworker_endpoint"] - - // Optional parameters. - // Example: 1 - minHealthyRdonlyTablets := parameters["min_healthy_rdonly_tablets"] - - var newTasks []*automationpb.TaskContainer - copySchemaTasks := NewTaskContainer() - for _, shard := range shards { - AddTask(copySchemaTasks, "CopySchemaShardTask", map[string]string{ - "source_keyspace_and_shard": topoproto.KeyspaceShardString(sourceKeyspace, shard), - "dest_keyspace_and_shard": topoproto.KeyspaceShardString(destKeyspace, shard), - "vtctld_endpoint": vtctldEndpoint, - "tables": tables, - }) - } - newTasks = append(newTasks, copySchemaTasks) - - vSplitCloneTasks := NewTaskContainer() - for _, shard := range shards { - // TODO(mberlin): Add a semaphore as argument to limit the parallism. - AddTask(vSplitCloneTasks, "VerticalSplitCloneTask", map[string]string{ - "dest_keyspace": destKeyspace, - "shard": shard, - "tables": tables, - "vtworker_endpoint": vtworkerEndpoint, - "min_healthy_rdonly_tablets": minHealthyRdonlyTablets, - }) - } - newTasks = append(newTasks, vSplitCloneTasks) - - // TODO(mberlin): When the framework supports nesting tasks, these wait tasks should be run before each SplitDiff. - waitTasks := NewTaskContainer() - for _, shard := range shards { - AddTask(waitTasks, "WaitForFilteredReplicationTask", map[string]string{ - "keyspace": destKeyspace, - "shard": shard, - "max_delay": "30s", - "vtctld_endpoint": vtctldEndpoint, - }) - } - newTasks = append(newTasks, waitTasks) - - // TODO(mberlin): Run all SplitDiffTasks in parallel which do not use overlapping source shards for the comparison. - for _, shard := range shards { - vSplitDiffTask := NewTaskContainer() - AddTask(vSplitDiffTask, "VerticalSplitDiffTask", map[string]string{ - "dest_keyspace": destKeyspace, - "shard": shard, - "vtworker_endpoint": vtworkerEndpoint, - "min_healthy_rdonly_tablets": minHealthyRdonlyTablets, - }) - newTasks = append(newTasks, vSplitDiffTask) - } - - for _, servedType := range []string{"rdonly", "replica", "primary"} { - migrateServedTypesTasks := NewTaskContainer() - for _, shard := range shards { - AddTask(migrateServedTypesTasks, "MigrateServedFromTask", map[string]string{ - "dest_keyspace": destKeyspace, - "shard": shard, - "type": servedType, - "vtctld_endpoint": vtctldEndpoint, - }) - } - newTasks = append(newTasks, migrateServedTypesTasks) - } - - return newTasks, "", nil -} - -// RequiredParameters is part of the Task interface. -func (t *VerticalSplitTask) RequiredParameters() []string { - return []string{"source_keyspace", "dest_keyspace", "shard_list", - "tables", "vtctld_endpoint", "vtworker_endpoint"} -} - -// OptionalParameters is part of the Task interface. -func (t *VerticalSplitTask) OptionalParameters() []string { - return []string{"min_healthy_rdonly_tablets"} -} diff --git a/go/vt/automation/vertical_split_task_test.go b/go/vt/automation/vertical_split_task_test.go deleted file mode 100644 index 2873ae87f1f..00000000000 --- a/go/vt/automation/vertical_split_task_test.go +++ /dev/null @@ -1,85 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package automation - -import ( - "flag" - "testing" - - "context" - - automationpb "vitess.io/vitess/go/vt/proto/automation" - "vitess.io/vitess/go/vt/vtctl/fakevtctlclient" - "vitess.io/vitess/go/vt/vtctl/vtctlclient" - "vitess.io/vitess/go/vt/worker/fakevtworkerclient" - "vitess.io/vitess/go/vt/worker/vtworkerclient" -) - -// TestVerticalSplitTask tests the vertical split cluster operation -// using mocked out vtctld and vtworker responses. -func TestVerticalSplitTask(t *testing.T) { - vtctld := fakevtctlclient.NewFakeVtctlClient() - vtctlclient.RegisterFactory("fake", vtctld.FakeVtctlClientFactory) - defer vtctlclient.UnregisterFactoryForTest("fake") - flag.Set("vtctl_client_protocol", "fake") - - vtworker := fakevtworkerclient.NewFakeVtworkerClient() - vtworkerclient.RegisterFactory("fake", vtworker.FakeVtworkerClientFactory) - defer vtworkerclient.UnregisterFactoryForTest("fake") - flag.Set("vtworker_client_protocol", "fake") - - vtctld.RegisterResult([]string{"CopySchemaShard", "--tables=table1,table2", "source_keyspace/0", "destination_keyspace/0"}, - "", // No output. - nil) // No error. - vtworker.RegisterResult([]string{"VerticalSplitClone", "--tables=table1,table2", "--min_healthy_rdonly_tablets=1", "destination_keyspace/0"}, "", nil) - vtctld.RegisterResult([]string{"WaitForFilteredReplication", "-max_delay", "30s", "destination_keyspace/0"}, "", nil) - vtworker.RegisterResult([]string{"VerticalSplitDiff", "--min_healthy_rdonly_tablets=1", "destination_keyspace/0"}, "", nil) - vtctld.RegisterResult([]string{"MigrateServedFrom", "destination_keyspace/0", "rdonly"}, "", nil) - vtctld.RegisterResult([]string{"MigrateServedFrom", "destination_keyspace/0", "replica"}, "", nil) - vtctld.RegisterResult([]string{"MigrateServedFrom", "destination_keyspace/0", "primary"}, - "ALL_DONE", - nil) - - scheduler, err := NewScheduler() - if err != nil { - t.Fatalf("Failed to create scheduler: %v", err) - } - defer scheduler.ShutdownAndWait() - - scheduler.Run() - - enqueueRequest := &automationpb.EnqueueClusterOperationRequest{ - Name: "VerticalSplitTask", - Parameters: map[string]string{ - "source_keyspace": "source_keyspace", - "dest_keyspace": "destination_keyspace", - "shard_list": "0", - "tables": "table1,table2", - "vtctld_endpoint": "localhost:15000", - "vtworker_endpoint": "localhost:15001", - "min_healthy_rdonly_tablets": "1", - }, - } - enqueueResponse, err := scheduler.EnqueueClusterOperation(context.Background(), enqueueRequest) - if err != nil { - t.Fatalf("Failed to start cluster operation. Request: %v Error: %v", enqueueRequest, err) - } - - waitForClusterOperation(t, scheduler, enqueueResponse.Id, - "ALL_DONE\n", - "" /* expected error */) -} diff --git a/go/vt/automation/vtctlclient_wrapper.go b/go/vt/automation/vtctlclient_wrapper.go deleted file mode 100644 index 5f0cb0d8d07..00000000000 --- a/go/vt/automation/vtctlclient_wrapper.go +++ /dev/null @@ -1,74 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package automation - -import ( - "bytes" - "fmt" - - "context" - - "vitess.io/vitess/go/vt/log" - "vitess.io/vitess/go/vt/logutil" - "vitess.io/vitess/go/vt/vtctl/vtctlclient" - - logutilpb "vitess.io/vitess/go/vt/proto/logutil" -) - -// ExecuteVtctl runs vtctl using vtctlclient. The stream of Event -// messages is concatenated into one output string. -// Additionally, the start and the end of the command will be logged to make -// it easier to debug which command was executed and how long it took. -func ExecuteVtctl(ctx context.Context, server string, args []string) (string, error) { - var output bytes.Buffer - loggerToBufferFunc := createLoggerEventToBufferFunction(&output) - outputLogger := newOutputLogger(loggerToBufferFunc) - - startMsg := fmt.Sprintf("Executing remote vtctl command: %v server: %v", args, server) - outputLogger.Infof(startMsg) - log.Info(startMsg) - - err := vtctlclient.RunCommandAndWait( - ctx, server, args, - loggerToBufferFunc) - - endMsg := fmt.Sprintf("Executed remote vtctl command: %v server: %v err: %v", args, server, err) - outputLogger.Infof(endMsg) - // Log full output to log file (but not to the buffer). - log.Infof("%v output (starting on next line):\n%v", endMsg, output.String()) - - return output.String(), err -} - -// createLoggerEventToBufferFunction returns a function to add LoggerEvent -// structs to a given buffer, one line per event. -// The buffer can be used to return a multi-line string with all events. -func createLoggerEventToBufferFunction(output *bytes.Buffer) func(*logutilpb.Event) { - return func(e *logutilpb.Event) { - logutil.EventToBuffer(e, output) - output.WriteRune('\n') - } -} - -// newOutputLogger returns a logger which makes it easy to log to a bytes.Buffer -// output. When calling this function, pass in the result of -// createLoggerEventToBufferFunction(). -func newOutputLogger(loggerToBufferFunc func(*logutilpb.Event)) logutil.Logger { - return logutil.NewCallbackLogger(func(e *logutilpb.Event) { - loggerToBufferFunc(e) - }) -} diff --git a/go/vt/automation/vtworkerclient_wrapper.go b/go/vt/automation/vtworkerclient_wrapper.go deleted file mode 100644 index 8ad3e52ce05..00000000000 --- a/go/vt/automation/vtworkerclient_wrapper.go +++ /dev/null @@ -1,53 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package automation - -import ( - "bytes" - "fmt" - - "context" - - "vitess.io/vitess/go/vt/log" - "vitess.io/vitess/go/vt/worker/vtworkerclient" -) - -// ExecuteVtworker executes the vtworker command in "args" via an RPC to -// "server". -// The output of the RPC, a stream of LoggerEvent messages, is concatenated into -// one output string. -// If a retryable error is encountered (e.g. the vtworker process is already -// executing another command), this function will keep retrying infinitely until -// "ctx" is cancelled. -func ExecuteVtworker(ctx context.Context, server string, args []string) (string, error) { - var output bytes.Buffer - loggerToBufferFunc := createLoggerEventToBufferFunction(&output) - outputLogger := newOutputLogger(loggerToBufferFunc) - - startMsg := fmt.Sprintf("Executing remote vtworker command: %v server: %v", args, server) - outputLogger.Infof(startMsg) - log.Info(startMsg) - - err := vtworkerclient.RunCommandAndWait(ctx, server, args, loggerToBufferFunc) - - endMsg := fmt.Sprintf("Executed remote vtworker command: %v server: %v err: %v", args, server, err) - outputLogger.Infof(endMsg) - // Log full output to log file (but not to the buffer). - log.Infof("%v output (starting on next line):\n%v", endMsg, output.String()) - - return output.String(), err -} diff --git a/go/vt/automation/wait_for_filtered_replication_task.go b/go/vt/automation/wait_for_filtered_replication_task.go deleted file mode 100644 index c5c6f911572..00000000000 --- a/go/vt/automation/wait_for_filtered_replication_task.go +++ /dev/null @@ -1,47 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package automation - -import ( - "context" - - automationpb "vitess.io/vitess/go/vt/proto/automation" - "vitess.io/vitess/go/vt/topo/topoproto" -) - -// WaitForFilteredReplicationTask runs vtctl WaitForFilteredReplication to block until the destination primary -// (i.e. the receiving side of the filtered replication) has caught up to max_delay with the source shard. -type WaitForFilteredReplicationTask struct { -} - -// Run is part of the Task interface. -func (t *WaitForFilteredReplicationTask) Run(parameters map[string]string) ([]*automationpb.TaskContainer, string, error) { - keyspaceAndShard := topoproto.KeyspaceShardString(parameters["keyspace"], parameters["shard"]) - output, err := ExecuteVtctl(context.TODO(), parameters["vtctld_endpoint"], - []string{"WaitForFilteredReplication", "-max_delay", parameters["max_delay"], keyspaceAndShard}) - return nil, output, err -} - -// RequiredParameters is part of the Task interface. -func (t *WaitForFilteredReplicationTask) RequiredParameters() []string { - return []string{"keyspace", "shard", "max_delay", "vtctld_endpoint"} -} - -// OptionalParameters is part of the Task interface. -func (t *WaitForFilteredReplicationTask) OptionalParameters() []string { - return nil -} diff --git a/go/vt/binlog/binlogplayer/binlog_player.go b/go/vt/binlog/binlogplayer/binlog_player.go index 7cfd92fa042..6c8a611b30c 100644 --- a/go/vt/binlog/binlogplayer/binlog_player.go +++ b/go/vt/binlog/binlogplayer/binlog_player.go @@ -521,7 +521,7 @@ func (blp *BinlogPlayer) setVReplicationState(state, message string) error { // CreateVReplicationTable returns the statements required to create // the _vt.vreplication table. // id: is an auto-increment column that identifies the stream. -// workflow: documents the creator/manager of the stream. Example: 'SplitClone'. +// workflow: documents the creator/manager of the stream. Example: 'MoveTables'. // source: contains a string proto representation of binlogpb.BinlogSource. // pos: initially, a start position, and is updated to the current position by the binlog player. // stop_pos: optional column that specifies the stop position. diff --git a/go/vt/binlog/keyspace_id_resolver.go b/go/vt/binlog/keyspace_id_resolver.go index 8b3c37c4f69..6903ba53b71 100644 --- a/go/vt/binlog/keyspace_id_resolver.go +++ b/go/vt/binlog/keyspace_id_resolver.go @@ -17,7 +17,6 @@ limitations under the License. package binlog import ( - "flag" "fmt" "strings" @@ -30,10 +29,6 @@ import ( "vitess.io/vitess/go/vt/vttablet/tabletserver/schema" ) -var ( - _ = flag.Bool("binlog_use_v3_resharding_mode", true, "(DEPRECATED) True if and only if the binlog streamer should use V3-style sharding, which doesn't require a preset sharding key column.") -) - // keyspaceIDResolver is constructed for a tableMap entry in RBR. It // is used for each row, and passed in the value used for figuring out // the keyspace id. diff --git a/go/vt/discovery/utils.go b/go/vt/discovery/utils.go index e7793731463..32faee8b083 100644 --- a/go/vt/discovery/utils.go +++ b/go/vt/discovery/utils.go @@ -33,10 +33,6 @@ func RemoveUnhealthyTablets(tabletStatsList []LegacyTabletStats) []LegacyTabletS result := make([]LegacyTabletStats, 0, len(tabletStatsList)) for _, ts := range tabletStatsList { // Note we do not check the 'Serving' flag here. - // This is mainly to avoid the case where we run a vtworker Diff between a - // source and destination, and the source is not serving (disabled by - // TabletControl). When we switch the tablet to 'worker', it will - // go back to serving state. if ts.Stats == nil || ts.Stats.HealthError != "" || ts.LastError != nil || LegacyIsReplicationLagHigh(&ts) { continue } diff --git a/go/vt/mysqlctl/mysqld.go b/go/vt/mysqlctl/mysqld.go index 52699917df0..e281f999bb1 100644 --- a/go/vt/mysqlctl/mysqld.go +++ b/go/vt/mysqlctl/mysqld.go @@ -59,8 +59,6 @@ var ( // 2. in vtctld so it can be exported to the UI (different // package, that's why it's exported). That way we can disable // menu items there, using features. - // 3. prevents the vtworker from updating replication topology - // after restarting replication after a split clone/diff. DisableActiveReparents = flag.Bool("disable_active_reparents", false, "if set, do not allow active reparents. Use this to protect a cluster using external reparents.") dbaPoolSize = flag.Int("dba_pool_size", 20, "Size of the connection pool for dba connections") diff --git a/go/vt/mysqlctl/rice-box.go b/go/vt/mysqlctl/rice-box.go index bfcacade105..4ec94bf2bb3 100644 --- a/go/vt/mysqlctl/rice-box.go +++ b/go/vt/mysqlctl/rice-box.go @@ -11,97 +11,97 @@ func init() { // define files file2 := &embedded.EmbeddedFile{ Filename: "gomysql.pc.tmpl", - FileModTime: time.Unix(1640048569, 0), + FileModTime: time.Unix(1625867173, 0), Content: string("Name: GoMysql\nDescription: Flags for using mysql C client in go\n"), } file3 := &embedded.EmbeddedFile{ Filename: "init_db.sql", - FileModTime: time.Unix(1653772923, 0), + FileModTime: time.Unix(1656561956, 0), Content: string("# This file is executed immediately after mysql_install_db,\n# to initialize a fresh data directory.\n\n###############################################################################\n# WARNING: This sql is *NOT* safe for production use,\n# as it contains default well-known users and passwords.\n# Care should be taken to change these users and passwords\n# for production.\n###############################################################################\n\n###############################################################################\n# Equivalent of mysql_secure_installation\n###############################################################################\n\n# Changes during the init db should not make it to the binlog.\n# They could potentially create errant transactions on replicas.\nSET sql_log_bin = 0;\n# Remove anonymous users.\nDELETE FROM mysql.user WHERE User = '';\n\n# Disable remote root access (only allow UNIX socket).\nDELETE FROM mysql.user WHERE User = 'root' AND Host != 'localhost';\n\n# Remove test database.\nDROP DATABASE IF EXISTS test;\n\n###############################################################################\n# Vitess defaults\n###############################################################################\n\n# Vitess-internal database.\nCREATE DATABASE IF NOT EXISTS _vt;\n# Note that definitions of local_metadata and shard_metadata should be the same\n# as in production which is defined in go/vt/mysqlctl/metadata_tables.go.\nCREATE TABLE IF NOT EXISTS _vt.local_metadata (\n name VARCHAR(255) NOT NULL,\n value VARCHAR(255) NOT NULL,\n db_name VARBINARY(255) NOT NULL,\n PRIMARY KEY (db_name, name)\n ) ENGINE=InnoDB;\nCREATE TABLE IF NOT EXISTS _vt.shard_metadata (\n name VARCHAR(255) NOT NULL,\n value MEDIUMBLOB NOT NULL,\n db_name VARBINARY(255) NOT NULL,\n PRIMARY KEY (db_name, name)\n ) ENGINE=InnoDB;\n\n# Admin user with all privileges.\nCREATE USER 'vt_dba'@'localhost';\nGRANT ALL ON *.* TO 'vt_dba'@'localhost';\nGRANT GRANT OPTION ON *.* TO 'vt_dba'@'localhost';\n\n# User for app traffic, with global read-write access.\nCREATE USER 'vt_app'@'localhost';\nGRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, RELOAD, PROCESS, FILE,\n REFERENCES, INDEX, ALTER, SHOW DATABASES, CREATE TEMPORARY TABLES,\n LOCK TABLES, EXECUTE, REPLICATION CLIENT, CREATE VIEW,\n SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, CREATE USER, EVENT, TRIGGER\n ON *.* TO 'vt_app'@'localhost';\n\n# User for app debug traffic, with global read access.\nCREATE USER 'vt_appdebug'@'localhost';\nGRANT SELECT, SHOW DATABASES, PROCESS ON *.* TO 'vt_appdebug'@'localhost';\n\n# User for administrative operations that need to be executed as non-SUPER.\n# Same permissions as vt_app here.\nCREATE USER 'vt_allprivs'@'localhost';\nGRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, RELOAD, PROCESS, FILE,\n REFERENCES, INDEX, ALTER, SHOW DATABASES, CREATE TEMPORARY TABLES,\n LOCK TABLES, EXECUTE, REPLICATION SLAVE, REPLICATION CLIENT, CREATE VIEW,\n SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, CREATE USER, EVENT, TRIGGER\n ON *.* TO 'vt_allprivs'@'localhost';\n\n# User for slave replication connections.\nCREATE USER 'vt_repl'@'%';\nGRANT REPLICATION SLAVE ON *.* TO 'vt_repl'@'%';\n\n# User for Vitess VReplication (base vstreamers and vplayer).\nCREATE USER 'vt_filtered'@'localhost';\nGRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, RELOAD, PROCESS, FILE,\n REFERENCES, INDEX, ALTER, SHOW DATABASES, CREATE TEMPORARY TABLES,\n LOCK TABLES, EXECUTE, REPLICATION SLAVE, REPLICATION CLIENT, CREATE VIEW,\n SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, CREATE USER, EVENT, TRIGGER\n ON *.* TO 'vt_filtered'@'localhost';\n\n# User for general MySQL monitoring.\nCREATE USER 'vt_monitoring'@'localhost';\nGRANT SELECT, PROCESS, SUPER, REPLICATION CLIENT, RELOAD\n ON *.* TO 'vt_monitoring'@'localhost';\nGRANT SELECT, UPDATE, DELETE, DROP\n ON performance_schema.* TO 'vt_monitoring'@'localhost';\n\n# User for Orchestrator (https://github.com/openark/orchestrator).\nCREATE USER 'orc_client_user'@'%' IDENTIFIED BY 'orc_client_user_password';\nGRANT SUPER, PROCESS, REPLICATION SLAVE, RELOAD\n ON *.* TO 'orc_client_user'@'%';\nGRANT SELECT\n ON _vt.* TO 'orc_client_user'@'%';\n\nFLUSH PRIVILEGES;\n\nRESET SLAVE ALL;\nRESET MASTER;\n"), } file5 := &embedded.EmbeddedFile{ Filename: "mycnf/default.cnf", - FileModTime: time.Unix(1640048569, 0), + FileModTime: time.Unix(1656560071, 0), Content: string("# Global configuration that is auto-included for all MySQL/MariaDB versions\n\ndatadir = {{.DataDir}}\ninnodb_data_home_dir = {{.InnodbDataHomeDir}}\ninnodb_log_group_home_dir = {{.InnodbLogGroupHomeDir}}\nlog-error = {{.ErrorLogPath}}\nlog-bin = {{.BinLogPath}}\nrelay-log = {{.RelayLogPath}}\nrelay-log-index = {{.RelayLogIndexPath}}\npid-file = {{.PidFile}}\nport = {{.MysqlPort}}\n\n{{if .SecureFilePriv}}\nsecure-file-priv = {{.SecureFilePriv}}\n{{end}}\n\n# all db instances should start in read-only mode - once the db is started and\n# fully functional, we'll push it into read-write mode\nread-only\nserver-id = {{.ServerID}}\n\n# all db instances should skip starting replication threads - that way we can do any\n# additional configuration (like enabling semi-sync) before we connect to\n# the source.\nskip_slave_start\nsocket = {{.SocketFile}}\ntmpdir = {{.TmpDir}}\n\nslow-query-log-file = {{.SlowLogPath}}\n\n# These are sensible defaults that apply to all MySQL/MariaDB versions\n\nlong_query_time = 2\nslow-query-log\nskip-name-resolve\nconnect_timeout = 30\ninnodb_lock_wait_timeout = 20\nmax_allowed_packet = 64M\nmax_connections = 500\n\n\n"), } file6 := &embedded.EmbeddedFile{ Filename: "mycnf/mariadb100.cnf", - FileModTime: time.Unix(1640048569, 0), + FileModTime: time.Unix(1636429180, 0), Content: string("# This file is auto-included when MariaDB 10.0 is detected.\n\n# Semi-sync replication is required for automated unplanned failover\n# (when the primary goes away). Here we just load the plugin so it's\n# available if desired, but it's disabled at startup.\n#\n# If the -enable_semi_sync flag is used, VTTablet will enable semi-sync\n# at the proper time when replication is set up, or when a primary is\n# promoted or demoted.\nplugin-load = rpl_semi_sync_master=semisync_master.so;rpl_semi_sync_slave=semisync_slave.so\n\nslave_net_timeout = 60\n\n# MariaDB 10.0 is unstrict by default\nsql_mode = STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION\n\n# enable strict mode so it's safe to compare sequence numbers across different server IDs.\ngtid_strict_mode = 1\ninnodb_stats_persistent = 0\n\n# When semi-sync is enabled, don't allow fallback to async\n# if you get no ack, or have no replicas. This is necessary to\n# prevent alternate futures when doing a failover in response to\n# a primary that becomes unresponsive.\nrpl_semi_sync_master_timeout = 1000000000000000000\nrpl_semi_sync_master_wait_no_slave = 1\n\n\ncharacter_set_server = utf8\ncollation_server = utf8_general_ci\n\nexpire_logs_days = 3\n\nsync_binlog = 1\nbinlog_format = ROW\nlog_slave_updates\nexpire_logs_days = 3\n\n# In MariaDB the default charset is latin1\n\ncharacter_set_server = utf8\ncollation_server = utf8_general_ci\n\n"), } file7 := &embedded.EmbeddedFile{ Filename: "mycnf/mariadb101.cnf", - FileModTime: time.Unix(1640048569, 0), + FileModTime: time.Unix(1636429180, 0), Content: string("# This file is auto-included when MariaDB 10.1 is detected.\n\n# Semi-sync replication is required for automated unplanned failover\n# (when the primary goes away). Here we just load the plugin so it's\n# available if desired, but it's disabled at startup.\n#\n# If the -enable_semi_sync flag is used, VTTablet will enable semi-sync\n# at the proper time when replication is set up, or when a primary is\n# promoted or demoted.\nplugin-load = rpl_semi_sync_master=semisync_master.so;rpl_semi_sync_slave=semisync_slave.so\n\nslave_net_timeout = 60\n\n# MariaDB 10.1 default is only no-engine-substitution and no-auto-create-user\nsql_mode = STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION,NO_AUTO_CREATE_USER\n\n# enable strict mode so it's safe to compare sequence numbers across different server IDs.\ngtid_strict_mode = 1\ninnodb_stats_persistent = 0\n\n# When semi-sync is enabled, don't allow fallback to async\n# if you get no ack, or have no replicas. This is necessary to\n# prevent alternate futures when doing a failover in response to\n# a primary that becomes unresponsive.\nrpl_semi_sync_master_timeout = 1000000000000000000\nrpl_semi_sync_master_wait_no_slave = 1\n\n\ncharacter_set_server = utf8\ncollation_server = utf8_general_ci\n\nexpire_logs_days = 3\n\nsync_binlog = 1\nbinlog_format = ROW\nlog_slave_updates\nexpire_logs_days = 3\n\n# In MariaDB the default charset is latin1\n\ncharacter_set_server = utf8\ncollation_server = utf8_general_ci\n"), } file8 := &embedded.EmbeddedFile{ Filename: "mycnf/mariadb102.cnf", - FileModTime: time.Unix(1640048569, 0), + FileModTime: time.Unix(1636429180, 0), Content: string("# This file is auto-included when MariaDB 10.2 is detected.\n\n# Semi-sync replication is required for automated unplanned failover\n# (when the primary goes away). Here we just load the plugin so it's\n# available if desired, but it's disabled at startup.\n#\n# If the -enable_semi_sync flag is used, VTTablet will enable semi-sync\n# at the proper time when replication is set up, or when a primary is\n# promoted or demoted.\nplugin-load = rpl_semi_sync_master=semisync_master.so;rpl_semi_sync_slave=semisync_slave.so\n\n# enable strict mode so it's safe to compare sequence numbers across different server IDs.\ngtid_strict_mode = 1\ninnodb_stats_persistent = 0\n\n# When semi-sync is enabled, don't allow fallback to async\n# if you get no ack, or have no replicas. This is necessary to\n# prevent alternate futures when doing a failover in response to\n# a primary that becomes unresponsive.\nrpl_semi_sync_master_timeout = 1000000000000000000\nrpl_semi_sync_master_wait_no_slave = 1\n\n\ncharacter_set_server = utf8\ncollation_server = utf8_general_ci\n\nexpire_logs_days = 3\n\nsync_binlog = 1\nbinlog_format = ROW\nlog_slave_updates\nexpire_logs_days = 3\n\n# In MariaDB the default charset is latin1\n\ncharacter_set_server = utf8\ncollation_server = utf8_general_ci\n"), } file9 := &embedded.EmbeddedFile{ Filename: "mycnf/mariadb103.cnf", - FileModTime: time.Unix(1640048569, 0), + FileModTime: time.Unix(1636429180, 0), Content: string("# This file is auto-included when MariaDB 10.3 is detected.\n\n# enable strict mode so it's safe to compare sequence numbers across different server IDs.\ngtid_strict_mode = 1\ninnodb_stats_persistent = 0\n\n# When semi-sync is enabled, don't allow fallback to async\n# if you get no ack, or have no replicas. This is necessary to\n# prevent alternate futures when doing a failover in response to\n# a primary that becomes unresponsive.\nrpl_semi_sync_master_timeout = 1000000000000000000\nrpl_semi_sync_master_wait_no_slave = 1\n\n\ncharacter_set_server = utf8\ncollation_server = utf8_general_ci\n\nexpire_logs_days = 3\n\nsync_binlog = 1\nbinlog_format = ROW\nlog_slave_updates\nexpire_logs_days = 3\n\n# In MariaDB the default charset is latin1\n\ncharacter_set_server = utf8\ncollation_server = utf8_general_ci\n\n\n"), } filea := &embedded.EmbeddedFile{ Filename: "mycnf/mariadb104.cnf", - FileModTime: time.Unix(1640048569, 0), + FileModTime: time.Unix(1636429180, 0), Content: string("# This file is auto-included when MariaDB 10.4 is detected.\n\n# enable strict mode so it's safe to compare sequence numbers across different server IDs.\ngtid_strict_mode = 1\ninnodb_stats_persistent = 0\n\n# When semi-sync is enabled, don't allow fallback to async\n# if you get no ack, or have no replicas. This is necessary to\n# prevent alternate futures when doing a failover in response to\n# a primary that becomes unresponsive.\nrpl_semi_sync_master_timeout = 1000000000000000000\nrpl_semi_sync_master_wait_no_slave = 1\n\n\ncharacter_set_server = utf8\ncollation_server = utf8_general_ci\n\nexpire_logs_days = 3\n\nsync_binlog = 1\nbinlog_format = ROW\nlog_slave_updates\nexpire_logs_days = 3\n\n# In MariaDB the default charset is latin1\n\ncharacter_set_server = utf8\ncollation_server = utf8_general_ci\n\n\n"), } fileb := &embedded.EmbeddedFile{ Filename: "mycnf/mysql57.cnf", - FileModTime: time.Unix(1653772923, 0), + FileModTime: time.Unix(1656826694, 0), Content: string("# This file is auto-included when MySQL 5.7 is detected.\n\n# MySQL 5.7 does not enable the binary log by default, and \n# info repositories default to file\n\ngtid_mode = ON\nlog_slave_updates\nenforce_gtid_consistency\nexpire_logs_days = 3\nmaster_info_repository = TABLE\nrelay_log_info_repository = TABLE\nrelay_log_purge = 1\nrelay_log_recovery = 1\n\n# In MySQL 5.7 the default charset is latin1\n\ncharacter_set_server = utf8\ncollation_server = utf8_general_ci\n\n# Semi-sync replication is required for automated unplanned failover\n# (when the primary goes away). Here we just load the plugin so it's\n# available if desired, but it's disabled at startup.\n#\n# If the -enable_semi_sync flag is used, VTTablet will enable semi-sync\n# at the proper time when replication is set up, or when a primary is\n# promoted or demoted.\nplugin-load = rpl_semi_sync_master=semisync_master.so;rpl_semi_sync_slave=semisync_slave.so\n\n# When semi-sync is enabled, don't allow fallback to async\n# if you get no ack, or have no replicas. This is necessary to\n# prevent alternate futures when doing a failover in response to\n# a primary that becomes unresponsive.\nrpl_semi_sync_master_timeout = 1000000000000000000\nrpl_semi_sync_master_wait_no_slave = 1\n\n"), } filec := &embedded.EmbeddedFile{ Filename: "mycnf/mysql80.cnf", - FileModTime: time.Unix(1653772923, 0), + FileModTime: time.Unix(1656826694, 0), Content: string("# This file is auto-included when MySQL 8.0 is detected.\n\n# MySQL 8.0 enables binlog by default with sync_binlog and TABLE info repositories\n# It does not enable GTIDs or enforced GTID consistency\n\ngtid_mode = ON\nenforce_gtid_consistency\nrelay_log_recovery = 1\nbinlog_expire_logs_seconds = 259200\n\n# disable mysqlx\nmysqlx = 0\n\n# 8.0 changes the default auth-plugin to caching_sha2_password\ndefault_authentication_plugin = mysql_native_password\n\n# Semi-sync replication is required for automated unplanned failover\n# (when the primary goes away). Here we just load the plugin so it's\n# available if desired, but it's disabled at startup.\n#\n# If the -enable_semi_sync flag is used, VTTablet will enable semi-sync\n# at the proper time when replication is set up, or when a primary is\n# promoted or demoted.\nplugin-load = rpl_semi_sync_master=semisync_master.so;rpl_semi_sync_slave=semisync_slave.so\n\n# MySQL 8.0 will not load plugins during --initialize\n# which makes these options unknown. Prefixing with --loose\n# tells the server it's fine if they are not understood.\nloose_rpl_semi_sync_master_timeout = 1000000000000000000\nloose_rpl_semi_sync_master_wait_no_slave = 1\n\n"), } filed := &embedded.EmbeddedFile{ Filename: "mycnf/sbr.cnf", - FileModTime: time.Unix(1640048569, 0), + FileModTime: time.Unix(1625867173, 0), Content: string("# This file is used to allow legacy tests to pass\n# In theory it should not be required\nbinlog_format=statement\n"), } filee := &embedded.EmbeddedFile{ Filename: "mycnf/test-suite.cnf", - FileModTime: time.Unix(1640048569, 0), + FileModTime: time.Unix(1636429180, 0), Content: string("# This sets some unsafe settings specifically for \n# the test-suite which is currently MySQL 5.7 based\n# In future it should be renamed testsuite.cnf\n\ninnodb_buffer_pool_size = 32M\ninnodb_flush_log_at_trx_commit = 0\ninnodb_log_buffer_size = 1M\ninnodb_log_file_size = 5M\n\n# Native AIO tends to run into aio-max-nr limit during test startup.\ninnodb_use_native_aio = 0\n\nkey_buffer_size = 2M\nsync_binlog=0\ninnodb_doublewrite=0\n\n# These two settings are required for the testsuite to pass, \n# but enabling them does not spark joy. They should be removed\n# in the future. See:\n# https://github.com/vitessio/vitess/issues/5396\n\nsql_mode = STRICT_TRANS_TABLES\n\n# set a short heartbeat interval in order to detect failures quickly\nslave_net_timeout = 4\n"), } fileg := &embedded.EmbeddedFile{ Filename: "orchestrator/default.json", - FileModTime: time.Unix(1640048569, 0), + FileModTime: time.Unix(1625867173, 0), Content: string("{\n \"Debug\": true,\n \"MySQLTopologyUser\": \"orc_client_user\",\n \"MySQLTopologyPassword\": \"orc_client_user_password\",\n \"MySQLReplicaUser\": \"vt_repl\",\n \"MySQLReplicaPassword\": \"\",\n \"RecoveryPeriodBlockSeconds\": 5\n}\n"), } filei := &embedded.EmbeddedFile{ Filename: "tablet/default.yaml", - FileModTime: time.Unix(1640048569, 0), + FileModTime: time.Unix(1636429180, 0), Content: string("tabletID: zone-1234\n\ninit:\n dbName: # init_db_name_override\n keyspace: # init_keyspace\n shard: # init_shard\n tabletType: # init_tablet_type\n timeoutSeconds: 60 # init_timeout\n\ndb:\n socket: # db_socket\n host: # db_host\n port: 0 # db_port\n charSet: # db_charset\n flags: 0 # db_flags\n flavor: # db_flavor\n sslCa: # db_ssl_ca\n sslCaPath: # db_ssl_ca_path\n sslCert: # db_ssl_cert\n sslKey: # db_ssl_key\n serverName: # db_server_name\n connectTimeoutMilliseconds: 0 # db_connect_timeout_ms\n app:\n user: vt_app # db_app_user\n password: # db_app_password\n useSsl: true # db_app_use_ssl\n preferTcp: false\n dba:\n user: vt_dba # db_dba_user\n password: # db_dba_password\n useSsl: true # db_dba_use_ssl\n preferTcp: false\n filtered:\n user: vt_filtered # db_filtered_user\n password: # db_filtered_password\n useSsl: true # db_filtered_use_ssl\n preferTcp: false\n repl:\n user: vt_repl # db_repl_user\n password: # db_repl_password\n useSsl: true # db_repl_use_ssl\n preferTcp: false\n appdebug:\n user: vt_appdebug # db_appdebug_user\n password: # db_appdebug_password\n useSsl: true # db_appdebug_use_ssl\n preferTcp: false\n allprivs:\n user: vt_allprivs # db_allprivs_user\n password: # db_allprivs_password\n useSsl: true # db_allprivs_use_ssl\n preferTcp: false\n\noltpReadPool:\n size: 16 # queryserver-config-pool-size\n timeoutSeconds: 0 # queryserver-config-query-pool-timeout\n idleTimeoutSeconds: 1800 # queryserver-config-idle-timeout\n prefillParallelism: 0 # queryserver-config-pool-prefill-parallelism\n maxWaiters: 50000 # queryserver-config-query-pool-waiter-cap\n\nolapReadPool:\n size: 200 # queryserver-config-stream-pool-size\n timeoutSeconds: 0 # queryserver-config-query-pool-timeout\n idleTimeoutSeconds: 1800 # queryserver-config-idle-timeout\n prefillParallelism: 0 # queryserver-config-stream-pool-prefill-parallelism\n maxWaiters: 0\n\ntxPool:\n size: 20 # queryserver-config-transaction-cap\n timeoutSeconds: 1 # queryserver-config-txpool-timeout\n idleTimeoutSeconds: 1800 # queryserver-config-idle-timeout\n prefillParallelism: 0 # queryserver-config-transaction-prefill-parallelism\n maxWaiters: 50000 # queryserver-config-txpool-waiter-cap\n\noltp:\n queryTimeoutSeconds: 30 # queryserver-config-query-timeout\n txTimeoutSeconds: 30 # queryserver-config-transaction-timeout\n maxRows: 10000 # queryserver-config-max-result-size\n warnRows: 0 # queryserver-config-warn-result-size\n\nhealthcheck:\n intervalSeconds: 20 # health_check_interval\n degradedThresholdSeconds: 30 # degraded_threshold\n unhealthyThresholdSeconds: 7200 # unhealthy_threshold\n\ngracePeriods:\n shutdownSeconds: 0 # shutdown_grace_period\n transitionSeconds: 0 # serving_state_grace_period\n\nreplicationTracker:\n mode: disable # enable_replication_reporter\n heartbeatIntervalMilliseconds: 0 # heartbeat_enable, heartbeat_interval\n\nhotRowProtection:\n mode: disable|dryRun|enable # enable_hot_row_protection, enable_hot_row_protection_dry_run\n # Recommended value: same as txPool.size.\n maxQueueSize: 20 # hot_row_protection_max_queue_size\n maxGlobalQueueSize: 1000 # hot_row_protection_max_global_queue_size\n maxConcurrency: 5 # hot_row_protection_concurrent_transactions\n\nconsolidator: enable|disable|notOnPrimary # enable-consolidator, enable-consolidator-replicas\npassthroughDML: false # queryserver-config-passthrough-dmls\nstreamBufferSize: 32768 # queryserver-config-stream-buffer-size\nqueryCacheSize: 5000 # queryserver-config-query-cache-size\nschemaReloadIntervalSeconds: 1800 # queryserver-config-schema-reload-time\nwatchReplication: false # watch_replication_stream\nterseErrors: false # queryserver-config-terse-errors\nmessagePostponeParallelism: 4 # queryserver-config-message-postpone-cap\ncacheResultFields: true # enable-query-plan-field-caching\n\n\n# The following flags are currently not supported.\n# enforce_strict_trans_tables\n# queryserver-config-strict-table-acl\n# queryserver-config-enable-table-acl-dry-run\n# queryserver-config-acl-exempt-acl\n# enable-tx-throttler\n# tx-throttler-config\n# tx-throttler-healthcheck-cells\n# enable_transaction_limit\n# enable_transaction_limit_dry_run\n# transaction_limit_per_user\n# transaction_limit_by_username\n# transaction_limit_by_principal\n# transaction_limit_by_component\n# transaction_limit_by_subcomponent\n"), } filej := &embedded.EmbeddedFile{ Filename: "zk-client-dev.json", - FileModTime: time.Unix(1640048569, 0), + FileModTime: time.Unix(1625867173, 0), Content: string("{\n \"local\": \"localhost:3863\",\n \"global\": \"localhost:3963\"\n}\n"), } filel := &embedded.EmbeddedFile{ Filename: "zkcfg/zoo.cfg", - FileModTime: time.Unix(1653772923, 0), + FileModTime: time.Unix(1653065346, 0), Content: string("tickTime=2000\ndataDir={{.DataDir}}\nclientPort={{.ClientPort}}\ninitLimit=5\nsyncLimit=2\nmaxClientCnxns=0\n# enable commands like ruok by default\n4lw.commands.whitelist=*\n{{range .Servers}}\nserver.{{.ServerId}}={{.Hostname}}:{{.LeaderPort}}:{{.ElectionPort}}\n{{end}}\n"), } @@ -109,7 +109,7 @@ func init() { // define dirs dir1 := &embedded.EmbeddedDir{ Filename: "", - DirModTime: time.Unix(1653772923, 0), + DirModTime: time.Unix(1656561956, 0), ChildFiles: []*embedded.EmbeddedFile{ file2, // "gomysql.pc.tmpl" file3, // "init_db.sql" @@ -119,7 +119,7 @@ func init() { } dir4 := &embedded.EmbeddedDir{ Filename: "mycnf", - DirModTime: time.Unix(1653772923, 0), + DirModTime: time.Unix(1656826694, 0), ChildFiles: []*embedded.EmbeddedFile{ file5, // "mycnf/default.cnf" file6, // "mycnf/mariadb100.cnf" @@ -136,7 +136,7 @@ func init() { } dirf := &embedded.EmbeddedDir{ Filename: "orchestrator", - DirModTime: time.Unix(1640048569, 0), + DirModTime: time.Unix(1625867173, 0), ChildFiles: []*embedded.EmbeddedFile{ fileg, // "orchestrator/default.json" @@ -144,7 +144,7 @@ func init() { } dirh := &embedded.EmbeddedDir{ Filename: "tablet", - DirModTime: time.Unix(1640048569, 0), + DirModTime: time.Unix(1636429180, 0), ChildFiles: []*embedded.EmbeddedFile{ filei, // "tablet/default.yaml" @@ -152,7 +152,7 @@ func init() { } dirk := &embedded.EmbeddedDir{ Filename: "zkcfg", - DirModTime: time.Unix(1653772923, 0), + DirModTime: time.Unix(1653065346, 0), ChildFiles: []*embedded.EmbeddedFile{ filel, // "zkcfg/zoo.cfg" @@ -175,7 +175,7 @@ func init() { // register embeddedBox embedded.RegisterEmbeddedBox(`../../../config`, &embedded.EmbeddedBox{ Name: `../../../config`, - Time: time.Unix(1653772923, 0), + Time: time.Unix(1656561956, 0), Dirs: map[string]*embedded.EmbeddedDir{ "": dir1, "mycnf": dir4, diff --git a/go/vt/proto/throttlerservice/throttlerservice.pb.go b/go/vt/proto/throttlerservice/throttlerservice.pb.go index 85431e6a4b1..3a0aebb89a6 100644 --- a/go/vt/proto/throttlerservice/throttlerservice.pb.go +++ b/go/vt/proto/throttlerservice/throttlerservice.pb.go @@ -14,8 +14,7 @@ //limitations under the License. // gRPC RPC interface for the internal resharding throttler (go/vt/throttler) -// which is used by the resharding clone process (vtworker) and filtered -// replication (vttablet). +// which is used by vreplication. // Code generated by protoc-gen-go. DO NOT EDIT. // versions: diff --git a/go/vt/proto/vtctlservice/vtctlservice.pb.go b/go/vt/proto/vtctlservice/vtctlservice.pb.go index 044d9672295..9690438f73e 100644 --- a/go/vt/proto/vtctlservice/vtctlservice.pb.go +++ b/go/vt/proto/vtctlservice/vtctlservice.pb.go @@ -51,7 +51,7 @@ var file_vtctlservice_proto_rawDesc = []byte{ 0x61, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x56, 0x74, 0x63, 0x74, 0x6c, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x32, 0x98, 0x39, 0x0a, 0x06, 0x56, 0x74, 0x63, 0x74, 0x6c, + 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x32, 0xaa, 0x38, 0x0a, 0x06, 0x56, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x12, 0x4e, 0x0a, 0x0b, 0x41, 0x64, 0x64, 0x43, 0x65, 0x6c, 0x6c, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1d, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x41, 0x64, 0x64, 0x43, 0x65, 0x6c, 0x6c, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, @@ -371,148 +371,141 @@ var file_vtctlservice_proto_rawDesc = []byte{ 0x63, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x53, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x44, 0x75, 0x72, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x50, 0x6f, 0x6c, 0x69, 0x63, - 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x6c, 0x0a, 0x15, 0x53, - 0x65, 0x74, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x64, - 0x46, 0x72, 0x6f, 0x6d, 0x12, 0x27, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, - 0x2e, 0x53, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x72, 0x76, - 0x65, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, - 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x53, 0x65, 0x74, 0x4b, 0x65, 0x79, - 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x75, 0x0a, 0x18, 0x53, 0x65, 0x74, - 0x53, 0x68, 0x61, 0x72, 0x64, 0x49, 0x73, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x53, 0x65, - 0x72, 0x76, 0x69, 0x6e, 0x67, 0x12, 0x2a, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, - 0x61, 0x2e, 0x53, 0x65, 0x74, 0x53, 0x68, 0x61, 0x72, 0x64, 0x49, 0x73, 0x50, 0x72, 0x69, 0x6d, - 0x61, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x2b, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x53, 0x65, - 0x74, 0x53, 0x68, 0x61, 0x72, 0x64, 0x49, 0x73, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x53, - 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, - 0x12, 0x6c, 0x0a, 0x15, 0x53, 0x65, 0x74, 0x53, 0x68, 0x61, 0x72, 0x64, 0x54, 0x61, 0x62, 0x6c, - 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x12, 0x27, 0x2e, 0x76, 0x74, 0x63, 0x74, - 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x53, 0x65, 0x74, 0x53, 0x68, 0x61, 0x72, 0x64, 0x54, 0x61, - 0x62, 0x6c, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x53, - 0x65, 0x74, 0x53, 0x68, 0x61, 0x72, 0x64, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x43, 0x6f, 0x6e, - 0x74, 0x72, 0x6f, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4e, - 0x0a, 0x0b, 0x53, 0x65, 0x74, 0x57, 0x72, 0x69, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x1d, 0x2e, - 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x53, 0x65, 0x74, 0x57, 0x72, 0x69, - 0x74, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x76, - 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x53, 0x65, 0x74, 0x57, 0x72, 0x69, 0x74, - 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x66, - 0x0a, 0x13, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x41, 0x64, 0x64, 0x12, 0x25, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, - 0x61, 0x2e, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x41, 0x64, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x76, - 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, - 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x64, 0x64, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x66, 0x0a, 0x13, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, - 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x69, 0x78, 0x12, 0x25, 0x2e, + 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x75, 0x0a, 0x18, 0x53, + 0x65, 0x74, 0x53, 0x68, 0x61, 0x72, 0x64, 0x49, 0x73, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, + 0x53, 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, 0x12, 0x2a, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, + 0x61, 0x74, 0x61, 0x2e, 0x53, 0x65, 0x74, 0x53, 0x68, 0x61, 0x72, 0x64, 0x49, 0x73, 0x50, 0x72, + 0x69, 0x6d, 0x61, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, + 0x53, 0x65, 0x74, 0x53, 0x68, 0x61, 0x72, 0x64, 0x49, 0x73, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, + 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x00, 0x12, 0x6c, 0x0a, 0x15, 0x53, 0x65, 0x74, 0x53, 0x68, 0x61, 0x72, 0x64, 0x54, 0x61, + 0x62, 0x6c, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x12, 0x27, 0x2e, 0x76, 0x74, + 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x53, 0x65, 0x74, 0x53, 0x68, 0x61, 0x72, 0x64, + 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, + 0x2e, 0x53, 0x65, 0x74, 0x53, 0x68, 0x61, 0x72, 0x64, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x43, + 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, + 0x12, 0x4e, 0x0a, 0x0b, 0x53, 0x65, 0x74, 0x57, 0x72, 0x69, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x12, + 0x1d, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x53, 0x65, 0x74, 0x57, + 0x72, 0x69, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, + 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x53, 0x65, 0x74, 0x57, 0x72, + 0x69, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, + 0x12, 0x66, 0x0a, 0x13, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x64, 0x64, 0x12, 0x25, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, + 0x61, 0x74, 0x61, 0x2e, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x64, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, + 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x53, 0x68, 0x61, 0x72, 0x64, + 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x64, 0x64, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x66, 0x0a, 0x13, 0x53, 0x68, 0x61, 0x72, + 0x64, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x69, 0x78, 0x12, + 0x25, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x53, 0x68, 0x61, 0x72, + 0x64, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x69, 0x78, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, + 0x74, 0x61, 0x2e, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x46, 0x69, 0x78, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, + 0x12, 0x78, 0x0a, 0x19, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x2b, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, - 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x69, 0x78, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, + 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x76, 0x74, 0x63, + 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x70, 0x6c, + 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x6f, 0x0a, 0x16, 0x53, 0x68, + 0x61, 0x72, 0x64, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, + 0x6d, 0x6f, 0x76, 0x65, 0x12, 0x28, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x46, 0x69, 0x78, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x78, - 0x0a, 0x19, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x2b, 0x2e, 0x76, 0x74, - 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x70, - 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, - 0x64, 0x61, 0x74, 0x61, 0x2e, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x6f, 0x0a, 0x16, 0x53, 0x68, 0x61, 0x72, - 0x64, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x6d, 0x6f, - 0x76, 0x65, 0x12, 0x28, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x53, - 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, - 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x76, - 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, - 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4e, 0x0a, 0x0b, 0x53, 0x6c, 0x65, - 0x65, 0x70, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x12, 0x1d, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, - 0x64, 0x61, 0x74, 0x61, 0x2e, 0x53, 0x6c, 0x65, 0x65, 0x70, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, - 0x61, 0x74, 0x61, 0x2e, 0x53, 0x6c, 0x65, 0x65, 0x70, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x57, 0x0a, 0x0e, 0x53, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x53, 0x68, 0x61, 0x72, 0x64, 0x41, 0x64, 0x64, 0x12, 0x20, 0x2e, 0x76, 0x74, - 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x68, - 0x61, 0x72, 0x64, 0x41, 0x64, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, + 0x6e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, + 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x53, 0x68, 0x61, 0x72, 0x64, + 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x6d, 0x6f, 0x76, + 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4e, 0x0a, 0x0b, 0x53, + 0x6c, 0x65, 0x65, 0x70, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x12, 0x1d, 0x2e, 0x76, 0x74, 0x63, + 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x53, 0x6c, 0x65, 0x65, 0x70, 0x54, 0x61, 0x62, 0x6c, + 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x76, 0x74, 0x63, 0x74, + 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x53, 0x6c, 0x65, 0x65, 0x70, 0x54, 0x61, 0x62, 0x6c, 0x65, + 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x57, 0x0a, 0x0e, 0x53, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x68, 0x61, 0x72, 0x64, 0x41, 0x64, 0x64, 0x12, 0x20, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x53, 0x68, 0x61, 0x72, 0x64, 0x41, 0x64, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x00, 0x12, 0x60, 0x0a, 0x11, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x68, 0x61, 0x72, - 0x64, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x23, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, - 0x61, 0x74, 0x61, 0x2e, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x68, 0x61, 0x72, 0x64, 0x44, - 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x76, - 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, - 0x68, 0x61, 0x72, 0x64, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x00, 0x12, 0x5d, 0x0a, 0x10, 0x53, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x70, - 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x22, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, - 0x64, 0x61, 0x74, 0x61, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x76, - 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, - 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x00, 0x12, 0x5a, 0x0a, 0x0f, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x70, 0x6c, 0x69, - 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, - 0x74, 0x61, 0x2e, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x76, 0x74, 0x63, 0x74, - 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, - 0x7b, 0x0a, 0x1a, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, - 0x6c, 0x6c, 0x79, 0x52, 0x65, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x65, 0x64, 0x12, 0x2c, 0x2e, + 0x53, 0x68, 0x61, 0x72, 0x64, 0x41, 0x64, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x21, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x53, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x53, 0x68, 0x61, 0x72, 0x64, 0x41, 0x64, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x00, 0x12, 0x60, 0x0a, 0x11, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x68, + 0x61, 0x72, 0x64, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x23, 0x2e, 0x76, 0x74, 0x63, 0x74, + 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x68, 0x61, 0x72, + 0x64, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, + 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x53, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x53, 0x68, 0x61, 0x72, 0x64, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5d, 0x0a, 0x10, 0x53, 0x74, 0x61, 0x72, 0x74, 0x52, + 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x22, 0x2e, 0x76, 0x74, 0x63, + 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x70, 0x6c, + 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, + 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x74, + 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5a, 0x0a, 0x0f, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x70, + 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, + 0x64, 0x61, 0x74, 0x61, 0x2e, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x76, 0x74, + 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x70, 0x6c, + 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x00, 0x12, 0x7b, 0x0a, 0x1a, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x45, 0x78, 0x74, 0x65, 0x72, + 0x6e, 0x61, 0x6c, 0x6c, 0x79, 0x52, 0x65, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x65, 0x64, 0x12, + 0x2c, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, + 0x65, 0x74, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x6c, 0x79, 0x52, 0x65, 0x70, 0x61, + 0x72, 0x65, 0x6e, 0x74, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x6c, 0x79, 0x52, 0x65, 0x70, 0x61, 0x72, 0x65, - 0x6e, 0x74, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x76, 0x74, - 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x45, 0x78, - 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x6c, 0x79, 0x52, 0x65, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, - 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x57, 0x0a, 0x0e, - 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x65, 0x6c, 0x6c, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x20, - 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x43, 0x65, 0x6c, 0x6c, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x21, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x55, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x43, 0x65, 0x6c, 0x6c, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5d, 0x0a, 0x10, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, - 0x65, 0x6c, 0x6c, 0x73, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x12, 0x22, 0x2e, 0x76, 0x74, 0x63, 0x74, - 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x65, 0x6c, 0x6c, - 0x73, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, - 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, - 0x43, 0x65, 0x6c, 0x6c, 0x73, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x00, 0x12, 0x45, 0x0a, 0x08, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, - 0x12, 0x1a, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x56, 0x61, 0x6c, - 0x69, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x76, - 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, - 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5d, 0x0a, 0x10, 0x56, - 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, - 0x22, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x56, 0x61, 0x6c, 0x69, - 0x64, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, - 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x6f, 0x0a, 0x16, 0x56, 0x61, - 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4b, 0x65, 0x79, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x12, 0x28, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, - 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4b, - 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, + 0x6e, 0x74, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x57, + 0x0a, 0x0e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x65, 0x6c, 0x6c, 0x49, 0x6e, 0x66, 0x6f, + 0x12, 0x20, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x55, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x43, 0x65, 0x6c, 0x6c, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x55, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x65, 0x6c, 0x6c, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5d, 0x0a, 0x10, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x43, 0x65, 0x6c, 0x6c, 0x73, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x12, 0x22, 0x2e, 0x76, 0x74, + 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x65, + 0x6c, 0x6c, 0x73, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x23, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x55, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x43, 0x65, 0x6c, 0x6c, 0x73, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x45, 0x0a, 0x08, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, + 0x74, 0x65, 0x12, 0x1a, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x56, + 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, + 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, + 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5d, 0x0a, + 0x10, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x12, 0x22, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x56, 0x61, + 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, + 0x61, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x6f, 0x0a, 0x16, + 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4b, 0x65, + 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x28, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, + 0x74, 0x61, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x53, 0x63, 0x68, 0x65, 0x6d, + 0x61, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x29, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x56, 0x61, 0x6c, + 0x69, 0x64, 0x61, 0x74, 0x65, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4b, 0x65, 0x79, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x54, 0x0a, + 0x0d, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x53, 0x68, 0x61, 0x72, 0x64, 0x12, 0x1f, + 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, + 0x61, 0x74, 0x65, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x20, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x56, 0x61, 0x6c, 0x69, + 0x64, 0x61, 0x74, 0x65, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x00, 0x12, 0x72, 0x0a, 0x17, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x56, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x29, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, - 0x61, 0x74, 0x65, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x54, 0x0a, 0x0d, 0x56, - 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x53, 0x68, 0x61, 0x72, 0x64, 0x12, 0x1f, 0x2e, 0x76, - 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, - 0x65, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, + 0x61, 0x74, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x76, 0x74, 0x63, 0x74, + 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x56, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5a, 0x0a, 0x0f, 0x56, 0x61, 0x6c, 0x69, 0x64, + 0x61, 0x74, 0x65, 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x21, 0x2e, 0x76, 0x74, 0x63, + 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x56, + 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, - 0x74, 0x65, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x00, 0x12, 0x72, 0x0a, 0x17, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x56, 0x65, 0x72, - 0x73, 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x29, 0x2e, 0x76, - 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, - 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, - 0x61, 0x74, 0x61, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x56, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5a, 0x0a, 0x0f, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, - 0x65, 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x21, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, - 0x64, 0x61, 0x74, 0x61, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x56, 0x53, 0x63, - 0x68, 0x65, 0x6d, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x76, 0x74, - 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, - 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x00, 0x42, 0x2b, 0x5a, 0x29, 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, 0x63, 0x74, 0x6c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x62, 0x06, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x74, 0x65, 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x00, 0x42, 0x2b, 0x5a, 0x29, 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, 0x63, 0x74, 0x6c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var file_vtctlservice_proto_goTypes = []interface{}{ @@ -574,107 +567,105 @@ var file_vtctlservice_proto_goTypes = []interface{}{ (*vtctldata.RestoreFromBackupRequest)(nil), // 55: vtctldata.RestoreFromBackupRequest (*vtctldata.RunHealthCheckRequest)(nil), // 56: vtctldata.RunHealthCheckRequest (*vtctldata.SetKeyspaceDurabilityPolicyRequest)(nil), // 57: vtctldata.SetKeyspaceDurabilityPolicyRequest - (*vtctldata.SetKeyspaceServedFromRequest)(nil), // 58: vtctldata.SetKeyspaceServedFromRequest - (*vtctldata.SetShardIsPrimaryServingRequest)(nil), // 59: vtctldata.SetShardIsPrimaryServingRequest - (*vtctldata.SetShardTabletControlRequest)(nil), // 60: vtctldata.SetShardTabletControlRequest - (*vtctldata.SetWritableRequest)(nil), // 61: vtctldata.SetWritableRequest - (*vtctldata.ShardReplicationAddRequest)(nil), // 62: vtctldata.ShardReplicationAddRequest - (*vtctldata.ShardReplicationFixRequest)(nil), // 63: vtctldata.ShardReplicationFixRequest - (*vtctldata.ShardReplicationPositionsRequest)(nil), // 64: vtctldata.ShardReplicationPositionsRequest - (*vtctldata.ShardReplicationRemoveRequest)(nil), // 65: vtctldata.ShardReplicationRemoveRequest - (*vtctldata.SleepTabletRequest)(nil), // 66: vtctldata.SleepTabletRequest - (*vtctldata.SourceShardAddRequest)(nil), // 67: vtctldata.SourceShardAddRequest - (*vtctldata.SourceShardDeleteRequest)(nil), // 68: vtctldata.SourceShardDeleteRequest - (*vtctldata.StartReplicationRequest)(nil), // 69: vtctldata.StartReplicationRequest - (*vtctldata.StopReplicationRequest)(nil), // 70: vtctldata.StopReplicationRequest - (*vtctldata.TabletExternallyReparentedRequest)(nil), // 71: vtctldata.TabletExternallyReparentedRequest - (*vtctldata.UpdateCellInfoRequest)(nil), // 72: vtctldata.UpdateCellInfoRequest - (*vtctldata.UpdateCellsAliasRequest)(nil), // 73: vtctldata.UpdateCellsAliasRequest - (*vtctldata.ValidateRequest)(nil), // 74: vtctldata.ValidateRequest - (*vtctldata.ValidateKeyspaceRequest)(nil), // 75: vtctldata.ValidateKeyspaceRequest - (*vtctldata.ValidateSchemaKeyspaceRequest)(nil), // 76: vtctldata.ValidateSchemaKeyspaceRequest - (*vtctldata.ValidateShardRequest)(nil), // 77: vtctldata.ValidateShardRequest - (*vtctldata.ValidateVersionKeyspaceRequest)(nil), // 78: vtctldata.ValidateVersionKeyspaceRequest - (*vtctldata.ValidateVSchemaRequest)(nil), // 79: vtctldata.ValidateVSchemaRequest - (*vtctldata.ExecuteVtctlCommandResponse)(nil), // 80: vtctldata.ExecuteVtctlCommandResponse - (*vtctldata.AddCellInfoResponse)(nil), // 81: vtctldata.AddCellInfoResponse - (*vtctldata.AddCellsAliasResponse)(nil), // 82: vtctldata.AddCellsAliasResponse - (*vtctldata.ApplyRoutingRulesResponse)(nil), // 83: vtctldata.ApplyRoutingRulesResponse - (*vtctldata.ApplySchemaResponse)(nil), // 84: vtctldata.ApplySchemaResponse - (*vtctldata.ApplyVSchemaResponse)(nil), // 85: vtctldata.ApplyVSchemaResponse - (*vtctldata.BackupResponse)(nil), // 86: vtctldata.BackupResponse - (*vtctldata.ChangeTabletTypeResponse)(nil), // 87: vtctldata.ChangeTabletTypeResponse - (*vtctldata.CreateKeyspaceResponse)(nil), // 88: vtctldata.CreateKeyspaceResponse - (*vtctldata.CreateShardResponse)(nil), // 89: vtctldata.CreateShardResponse - (*vtctldata.DeleteCellInfoResponse)(nil), // 90: vtctldata.DeleteCellInfoResponse - (*vtctldata.DeleteCellsAliasResponse)(nil), // 91: vtctldata.DeleteCellsAliasResponse - (*vtctldata.DeleteKeyspaceResponse)(nil), // 92: vtctldata.DeleteKeyspaceResponse - (*vtctldata.DeleteShardsResponse)(nil), // 93: vtctldata.DeleteShardsResponse - (*vtctldata.DeleteSrvVSchemaResponse)(nil), // 94: vtctldata.DeleteSrvVSchemaResponse - (*vtctldata.DeleteTabletsResponse)(nil), // 95: vtctldata.DeleteTabletsResponse - (*vtctldata.EmergencyReparentShardResponse)(nil), // 96: vtctldata.EmergencyReparentShardResponse - (*vtctldata.ExecuteFetchAsAppResponse)(nil), // 97: vtctldata.ExecuteFetchAsAppResponse - (*vtctldata.ExecuteFetchAsDBAResponse)(nil), // 98: vtctldata.ExecuteFetchAsDBAResponse - (*vtctldata.ExecuteHookResponse)(nil), // 99: vtctldata.ExecuteHookResponse - (*vtctldata.FindAllShardsInKeyspaceResponse)(nil), // 100: vtctldata.FindAllShardsInKeyspaceResponse - (*vtctldata.GetBackupsResponse)(nil), // 101: vtctldata.GetBackupsResponse - (*vtctldata.GetCellInfoResponse)(nil), // 102: vtctldata.GetCellInfoResponse - (*vtctldata.GetCellInfoNamesResponse)(nil), // 103: vtctldata.GetCellInfoNamesResponse - (*vtctldata.GetCellsAliasesResponse)(nil), // 104: vtctldata.GetCellsAliasesResponse - (*vtctldata.GetKeyspaceResponse)(nil), // 105: vtctldata.GetKeyspaceResponse - (*vtctldata.GetKeyspacesResponse)(nil), // 106: vtctldata.GetKeyspacesResponse - (*vtctldata.GetPermissionsResponse)(nil), // 107: vtctldata.GetPermissionsResponse - (*vtctldata.GetRoutingRulesResponse)(nil), // 108: vtctldata.GetRoutingRulesResponse - (*vtctldata.GetSchemaResponse)(nil), // 109: vtctldata.GetSchemaResponse - (*vtctldata.GetShardResponse)(nil), // 110: vtctldata.GetShardResponse - (*vtctldata.GetSrvKeyspaceNamesResponse)(nil), // 111: vtctldata.GetSrvKeyspaceNamesResponse - (*vtctldata.GetSrvKeyspacesResponse)(nil), // 112: vtctldata.GetSrvKeyspacesResponse - (*vtctldata.GetSrvVSchemaResponse)(nil), // 113: vtctldata.GetSrvVSchemaResponse - (*vtctldata.GetSrvVSchemasResponse)(nil), // 114: vtctldata.GetSrvVSchemasResponse - (*vtctldata.GetTabletResponse)(nil), // 115: vtctldata.GetTabletResponse - (*vtctldata.GetTabletsResponse)(nil), // 116: vtctldata.GetTabletsResponse - (*vtctldata.GetVersionResponse)(nil), // 117: vtctldata.GetVersionResponse - (*vtctldata.GetVSchemaResponse)(nil), // 118: vtctldata.GetVSchemaResponse - (*vtctldata.GetWorkflowsResponse)(nil), // 119: vtctldata.GetWorkflowsResponse - (*vtctldata.InitShardPrimaryResponse)(nil), // 120: vtctldata.InitShardPrimaryResponse - (*vtctldata.PingTabletResponse)(nil), // 121: vtctldata.PingTabletResponse - (*vtctldata.PlannedReparentShardResponse)(nil), // 122: vtctldata.PlannedReparentShardResponse - (*vtctldata.RebuildKeyspaceGraphResponse)(nil), // 123: vtctldata.RebuildKeyspaceGraphResponse - (*vtctldata.RebuildVSchemaGraphResponse)(nil), // 124: vtctldata.RebuildVSchemaGraphResponse - (*vtctldata.RefreshStateResponse)(nil), // 125: vtctldata.RefreshStateResponse - (*vtctldata.RefreshStateByShardResponse)(nil), // 126: vtctldata.RefreshStateByShardResponse - (*vtctldata.ReloadSchemaResponse)(nil), // 127: vtctldata.ReloadSchemaResponse - (*vtctldata.ReloadSchemaKeyspaceResponse)(nil), // 128: vtctldata.ReloadSchemaKeyspaceResponse - (*vtctldata.ReloadSchemaShardResponse)(nil), // 129: vtctldata.ReloadSchemaShardResponse - (*vtctldata.RemoveBackupResponse)(nil), // 130: vtctldata.RemoveBackupResponse - (*vtctldata.RemoveKeyspaceCellResponse)(nil), // 131: vtctldata.RemoveKeyspaceCellResponse - (*vtctldata.RemoveShardCellResponse)(nil), // 132: vtctldata.RemoveShardCellResponse - (*vtctldata.ReparentTabletResponse)(nil), // 133: vtctldata.ReparentTabletResponse - (*vtctldata.RestoreFromBackupResponse)(nil), // 134: vtctldata.RestoreFromBackupResponse - (*vtctldata.RunHealthCheckResponse)(nil), // 135: vtctldata.RunHealthCheckResponse - (*vtctldata.SetKeyspaceDurabilityPolicyResponse)(nil), // 136: vtctldata.SetKeyspaceDurabilityPolicyResponse - (*vtctldata.SetKeyspaceServedFromResponse)(nil), // 137: vtctldata.SetKeyspaceServedFromResponse - (*vtctldata.SetShardIsPrimaryServingResponse)(nil), // 138: vtctldata.SetShardIsPrimaryServingResponse - (*vtctldata.SetShardTabletControlResponse)(nil), // 139: vtctldata.SetShardTabletControlResponse - (*vtctldata.SetWritableResponse)(nil), // 140: vtctldata.SetWritableResponse - (*vtctldata.ShardReplicationAddResponse)(nil), // 141: vtctldata.ShardReplicationAddResponse - (*vtctldata.ShardReplicationFixResponse)(nil), // 142: vtctldata.ShardReplicationFixResponse - (*vtctldata.ShardReplicationPositionsResponse)(nil), // 143: vtctldata.ShardReplicationPositionsResponse - (*vtctldata.ShardReplicationRemoveResponse)(nil), // 144: vtctldata.ShardReplicationRemoveResponse - (*vtctldata.SleepTabletResponse)(nil), // 145: vtctldata.SleepTabletResponse - (*vtctldata.SourceShardAddResponse)(nil), // 146: vtctldata.SourceShardAddResponse - (*vtctldata.SourceShardDeleteResponse)(nil), // 147: vtctldata.SourceShardDeleteResponse - (*vtctldata.StartReplicationResponse)(nil), // 148: vtctldata.StartReplicationResponse - (*vtctldata.StopReplicationResponse)(nil), // 149: vtctldata.StopReplicationResponse - (*vtctldata.TabletExternallyReparentedResponse)(nil), // 150: vtctldata.TabletExternallyReparentedResponse - (*vtctldata.UpdateCellInfoResponse)(nil), // 151: vtctldata.UpdateCellInfoResponse - (*vtctldata.UpdateCellsAliasResponse)(nil), // 152: vtctldata.UpdateCellsAliasResponse - (*vtctldata.ValidateResponse)(nil), // 153: vtctldata.ValidateResponse - (*vtctldata.ValidateKeyspaceResponse)(nil), // 154: vtctldata.ValidateKeyspaceResponse - (*vtctldata.ValidateSchemaKeyspaceResponse)(nil), // 155: vtctldata.ValidateSchemaKeyspaceResponse - (*vtctldata.ValidateShardResponse)(nil), // 156: vtctldata.ValidateShardResponse - (*vtctldata.ValidateVersionKeyspaceResponse)(nil), // 157: vtctldata.ValidateVersionKeyspaceResponse - (*vtctldata.ValidateVSchemaResponse)(nil), // 158: vtctldata.ValidateVSchemaResponse + (*vtctldata.SetShardIsPrimaryServingRequest)(nil), // 58: vtctldata.SetShardIsPrimaryServingRequest + (*vtctldata.SetShardTabletControlRequest)(nil), // 59: vtctldata.SetShardTabletControlRequest + (*vtctldata.SetWritableRequest)(nil), // 60: vtctldata.SetWritableRequest + (*vtctldata.ShardReplicationAddRequest)(nil), // 61: vtctldata.ShardReplicationAddRequest + (*vtctldata.ShardReplicationFixRequest)(nil), // 62: vtctldata.ShardReplicationFixRequest + (*vtctldata.ShardReplicationPositionsRequest)(nil), // 63: vtctldata.ShardReplicationPositionsRequest + (*vtctldata.ShardReplicationRemoveRequest)(nil), // 64: vtctldata.ShardReplicationRemoveRequest + (*vtctldata.SleepTabletRequest)(nil), // 65: vtctldata.SleepTabletRequest + (*vtctldata.SourceShardAddRequest)(nil), // 66: vtctldata.SourceShardAddRequest + (*vtctldata.SourceShardDeleteRequest)(nil), // 67: vtctldata.SourceShardDeleteRequest + (*vtctldata.StartReplicationRequest)(nil), // 68: vtctldata.StartReplicationRequest + (*vtctldata.StopReplicationRequest)(nil), // 69: vtctldata.StopReplicationRequest + (*vtctldata.TabletExternallyReparentedRequest)(nil), // 70: vtctldata.TabletExternallyReparentedRequest + (*vtctldata.UpdateCellInfoRequest)(nil), // 71: vtctldata.UpdateCellInfoRequest + (*vtctldata.UpdateCellsAliasRequest)(nil), // 72: vtctldata.UpdateCellsAliasRequest + (*vtctldata.ValidateRequest)(nil), // 73: vtctldata.ValidateRequest + (*vtctldata.ValidateKeyspaceRequest)(nil), // 74: vtctldata.ValidateKeyspaceRequest + (*vtctldata.ValidateSchemaKeyspaceRequest)(nil), // 75: vtctldata.ValidateSchemaKeyspaceRequest + (*vtctldata.ValidateShardRequest)(nil), // 76: vtctldata.ValidateShardRequest + (*vtctldata.ValidateVersionKeyspaceRequest)(nil), // 77: vtctldata.ValidateVersionKeyspaceRequest + (*vtctldata.ValidateVSchemaRequest)(nil), // 78: vtctldata.ValidateVSchemaRequest + (*vtctldata.ExecuteVtctlCommandResponse)(nil), // 79: vtctldata.ExecuteVtctlCommandResponse + (*vtctldata.AddCellInfoResponse)(nil), // 80: vtctldata.AddCellInfoResponse + (*vtctldata.AddCellsAliasResponse)(nil), // 81: vtctldata.AddCellsAliasResponse + (*vtctldata.ApplyRoutingRulesResponse)(nil), // 82: vtctldata.ApplyRoutingRulesResponse + (*vtctldata.ApplySchemaResponse)(nil), // 83: vtctldata.ApplySchemaResponse + (*vtctldata.ApplyVSchemaResponse)(nil), // 84: vtctldata.ApplyVSchemaResponse + (*vtctldata.BackupResponse)(nil), // 85: vtctldata.BackupResponse + (*vtctldata.ChangeTabletTypeResponse)(nil), // 86: vtctldata.ChangeTabletTypeResponse + (*vtctldata.CreateKeyspaceResponse)(nil), // 87: vtctldata.CreateKeyspaceResponse + (*vtctldata.CreateShardResponse)(nil), // 88: vtctldata.CreateShardResponse + (*vtctldata.DeleteCellInfoResponse)(nil), // 89: vtctldata.DeleteCellInfoResponse + (*vtctldata.DeleteCellsAliasResponse)(nil), // 90: vtctldata.DeleteCellsAliasResponse + (*vtctldata.DeleteKeyspaceResponse)(nil), // 91: vtctldata.DeleteKeyspaceResponse + (*vtctldata.DeleteShardsResponse)(nil), // 92: vtctldata.DeleteShardsResponse + (*vtctldata.DeleteSrvVSchemaResponse)(nil), // 93: vtctldata.DeleteSrvVSchemaResponse + (*vtctldata.DeleteTabletsResponse)(nil), // 94: vtctldata.DeleteTabletsResponse + (*vtctldata.EmergencyReparentShardResponse)(nil), // 95: vtctldata.EmergencyReparentShardResponse + (*vtctldata.ExecuteFetchAsAppResponse)(nil), // 96: vtctldata.ExecuteFetchAsAppResponse + (*vtctldata.ExecuteFetchAsDBAResponse)(nil), // 97: vtctldata.ExecuteFetchAsDBAResponse + (*vtctldata.ExecuteHookResponse)(nil), // 98: vtctldata.ExecuteHookResponse + (*vtctldata.FindAllShardsInKeyspaceResponse)(nil), // 99: vtctldata.FindAllShardsInKeyspaceResponse + (*vtctldata.GetBackupsResponse)(nil), // 100: vtctldata.GetBackupsResponse + (*vtctldata.GetCellInfoResponse)(nil), // 101: vtctldata.GetCellInfoResponse + (*vtctldata.GetCellInfoNamesResponse)(nil), // 102: vtctldata.GetCellInfoNamesResponse + (*vtctldata.GetCellsAliasesResponse)(nil), // 103: vtctldata.GetCellsAliasesResponse + (*vtctldata.GetKeyspaceResponse)(nil), // 104: vtctldata.GetKeyspaceResponse + (*vtctldata.GetKeyspacesResponse)(nil), // 105: vtctldata.GetKeyspacesResponse + (*vtctldata.GetPermissionsResponse)(nil), // 106: vtctldata.GetPermissionsResponse + (*vtctldata.GetRoutingRulesResponse)(nil), // 107: vtctldata.GetRoutingRulesResponse + (*vtctldata.GetSchemaResponse)(nil), // 108: vtctldata.GetSchemaResponse + (*vtctldata.GetShardResponse)(nil), // 109: vtctldata.GetShardResponse + (*vtctldata.GetSrvKeyspaceNamesResponse)(nil), // 110: vtctldata.GetSrvKeyspaceNamesResponse + (*vtctldata.GetSrvKeyspacesResponse)(nil), // 111: vtctldata.GetSrvKeyspacesResponse + (*vtctldata.GetSrvVSchemaResponse)(nil), // 112: vtctldata.GetSrvVSchemaResponse + (*vtctldata.GetSrvVSchemasResponse)(nil), // 113: vtctldata.GetSrvVSchemasResponse + (*vtctldata.GetTabletResponse)(nil), // 114: vtctldata.GetTabletResponse + (*vtctldata.GetTabletsResponse)(nil), // 115: vtctldata.GetTabletsResponse + (*vtctldata.GetVersionResponse)(nil), // 116: vtctldata.GetVersionResponse + (*vtctldata.GetVSchemaResponse)(nil), // 117: vtctldata.GetVSchemaResponse + (*vtctldata.GetWorkflowsResponse)(nil), // 118: vtctldata.GetWorkflowsResponse + (*vtctldata.InitShardPrimaryResponse)(nil), // 119: vtctldata.InitShardPrimaryResponse + (*vtctldata.PingTabletResponse)(nil), // 120: vtctldata.PingTabletResponse + (*vtctldata.PlannedReparentShardResponse)(nil), // 121: vtctldata.PlannedReparentShardResponse + (*vtctldata.RebuildKeyspaceGraphResponse)(nil), // 122: vtctldata.RebuildKeyspaceGraphResponse + (*vtctldata.RebuildVSchemaGraphResponse)(nil), // 123: vtctldata.RebuildVSchemaGraphResponse + (*vtctldata.RefreshStateResponse)(nil), // 124: vtctldata.RefreshStateResponse + (*vtctldata.RefreshStateByShardResponse)(nil), // 125: vtctldata.RefreshStateByShardResponse + (*vtctldata.ReloadSchemaResponse)(nil), // 126: vtctldata.ReloadSchemaResponse + (*vtctldata.ReloadSchemaKeyspaceResponse)(nil), // 127: vtctldata.ReloadSchemaKeyspaceResponse + (*vtctldata.ReloadSchemaShardResponse)(nil), // 128: vtctldata.ReloadSchemaShardResponse + (*vtctldata.RemoveBackupResponse)(nil), // 129: vtctldata.RemoveBackupResponse + (*vtctldata.RemoveKeyspaceCellResponse)(nil), // 130: vtctldata.RemoveKeyspaceCellResponse + (*vtctldata.RemoveShardCellResponse)(nil), // 131: vtctldata.RemoveShardCellResponse + (*vtctldata.ReparentTabletResponse)(nil), // 132: vtctldata.ReparentTabletResponse + (*vtctldata.RestoreFromBackupResponse)(nil), // 133: vtctldata.RestoreFromBackupResponse + (*vtctldata.RunHealthCheckResponse)(nil), // 134: vtctldata.RunHealthCheckResponse + (*vtctldata.SetKeyspaceDurabilityPolicyResponse)(nil), // 135: vtctldata.SetKeyspaceDurabilityPolicyResponse + (*vtctldata.SetShardIsPrimaryServingResponse)(nil), // 136: vtctldata.SetShardIsPrimaryServingResponse + (*vtctldata.SetShardTabletControlResponse)(nil), // 137: vtctldata.SetShardTabletControlResponse + (*vtctldata.SetWritableResponse)(nil), // 138: vtctldata.SetWritableResponse + (*vtctldata.ShardReplicationAddResponse)(nil), // 139: vtctldata.ShardReplicationAddResponse + (*vtctldata.ShardReplicationFixResponse)(nil), // 140: vtctldata.ShardReplicationFixResponse + (*vtctldata.ShardReplicationPositionsResponse)(nil), // 141: vtctldata.ShardReplicationPositionsResponse + (*vtctldata.ShardReplicationRemoveResponse)(nil), // 142: vtctldata.ShardReplicationRemoveResponse + (*vtctldata.SleepTabletResponse)(nil), // 143: vtctldata.SleepTabletResponse + (*vtctldata.SourceShardAddResponse)(nil), // 144: vtctldata.SourceShardAddResponse + (*vtctldata.SourceShardDeleteResponse)(nil), // 145: vtctldata.SourceShardDeleteResponse + (*vtctldata.StartReplicationResponse)(nil), // 146: vtctldata.StartReplicationResponse + (*vtctldata.StopReplicationResponse)(nil), // 147: vtctldata.StopReplicationResponse + (*vtctldata.TabletExternallyReparentedResponse)(nil), // 148: vtctldata.TabletExternallyReparentedResponse + (*vtctldata.UpdateCellInfoResponse)(nil), // 149: vtctldata.UpdateCellInfoResponse + (*vtctldata.UpdateCellsAliasResponse)(nil), // 150: vtctldata.UpdateCellsAliasResponse + (*vtctldata.ValidateResponse)(nil), // 151: vtctldata.ValidateResponse + (*vtctldata.ValidateKeyspaceResponse)(nil), // 152: vtctldata.ValidateKeyspaceResponse + (*vtctldata.ValidateSchemaKeyspaceResponse)(nil), // 153: vtctldata.ValidateSchemaKeyspaceResponse + (*vtctldata.ValidateShardResponse)(nil), // 154: vtctldata.ValidateShardResponse + (*vtctldata.ValidateVersionKeyspaceResponse)(nil), // 155: vtctldata.ValidateVersionKeyspaceResponse + (*vtctldata.ValidateVSchemaResponse)(nil), // 156: vtctldata.ValidateVSchemaResponse } var file_vtctlservice_proto_depIdxs = []int32{ 0, // 0: vtctlservice.Vtctl.ExecuteVtctlCommand:input_type -> vtctldata.ExecuteVtctlCommandRequest @@ -735,110 +726,108 @@ var file_vtctlservice_proto_depIdxs = []int32{ 55, // 55: vtctlservice.Vtctld.RestoreFromBackup:input_type -> vtctldata.RestoreFromBackupRequest 56, // 56: vtctlservice.Vtctld.RunHealthCheck:input_type -> vtctldata.RunHealthCheckRequest 57, // 57: vtctlservice.Vtctld.SetKeyspaceDurabilityPolicy:input_type -> vtctldata.SetKeyspaceDurabilityPolicyRequest - 58, // 58: vtctlservice.Vtctld.SetKeyspaceServedFrom:input_type -> vtctldata.SetKeyspaceServedFromRequest - 59, // 59: vtctlservice.Vtctld.SetShardIsPrimaryServing:input_type -> vtctldata.SetShardIsPrimaryServingRequest - 60, // 60: vtctlservice.Vtctld.SetShardTabletControl:input_type -> vtctldata.SetShardTabletControlRequest - 61, // 61: vtctlservice.Vtctld.SetWritable:input_type -> vtctldata.SetWritableRequest - 62, // 62: vtctlservice.Vtctld.ShardReplicationAdd:input_type -> vtctldata.ShardReplicationAddRequest - 63, // 63: vtctlservice.Vtctld.ShardReplicationFix:input_type -> vtctldata.ShardReplicationFixRequest - 64, // 64: vtctlservice.Vtctld.ShardReplicationPositions:input_type -> vtctldata.ShardReplicationPositionsRequest - 65, // 65: vtctlservice.Vtctld.ShardReplicationRemove:input_type -> vtctldata.ShardReplicationRemoveRequest - 66, // 66: vtctlservice.Vtctld.SleepTablet:input_type -> vtctldata.SleepTabletRequest - 67, // 67: vtctlservice.Vtctld.SourceShardAdd:input_type -> vtctldata.SourceShardAddRequest - 68, // 68: vtctlservice.Vtctld.SourceShardDelete:input_type -> vtctldata.SourceShardDeleteRequest - 69, // 69: vtctlservice.Vtctld.StartReplication:input_type -> vtctldata.StartReplicationRequest - 70, // 70: vtctlservice.Vtctld.StopReplication:input_type -> vtctldata.StopReplicationRequest - 71, // 71: vtctlservice.Vtctld.TabletExternallyReparented:input_type -> vtctldata.TabletExternallyReparentedRequest - 72, // 72: vtctlservice.Vtctld.UpdateCellInfo:input_type -> vtctldata.UpdateCellInfoRequest - 73, // 73: vtctlservice.Vtctld.UpdateCellsAlias:input_type -> vtctldata.UpdateCellsAliasRequest - 74, // 74: vtctlservice.Vtctld.Validate:input_type -> vtctldata.ValidateRequest - 75, // 75: vtctlservice.Vtctld.ValidateKeyspace:input_type -> vtctldata.ValidateKeyspaceRequest - 76, // 76: vtctlservice.Vtctld.ValidateSchemaKeyspace:input_type -> vtctldata.ValidateSchemaKeyspaceRequest - 77, // 77: vtctlservice.Vtctld.ValidateShard:input_type -> vtctldata.ValidateShardRequest - 78, // 78: vtctlservice.Vtctld.ValidateVersionKeyspace:input_type -> vtctldata.ValidateVersionKeyspaceRequest - 79, // 79: vtctlservice.Vtctld.ValidateVSchema:input_type -> vtctldata.ValidateVSchemaRequest - 80, // 80: vtctlservice.Vtctl.ExecuteVtctlCommand:output_type -> vtctldata.ExecuteVtctlCommandResponse - 81, // 81: vtctlservice.Vtctld.AddCellInfo:output_type -> vtctldata.AddCellInfoResponse - 82, // 82: vtctlservice.Vtctld.AddCellsAlias:output_type -> vtctldata.AddCellsAliasResponse - 83, // 83: vtctlservice.Vtctld.ApplyRoutingRules:output_type -> vtctldata.ApplyRoutingRulesResponse - 84, // 84: vtctlservice.Vtctld.ApplySchema:output_type -> vtctldata.ApplySchemaResponse - 85, // 85: vtctlservice.Vtctld.ApplyVSchema:output_type -> vtctldata.ApplyVSchemaResponse - 86, // 86: vtctlservice.Vtctld.Backup:output_type -> vtctldata.BackupResponse - 86, // 87: vtctlservice.Vtctld.BackupShard:output_type -> vtctldata.BackupResponse - 87, // 88: vtctlservice.Vtctld.ChangeTabletType:output_type -> vtctldata.ChangeTabletTypeResponse - 88, // 89: vtctlservice.Vtctld.CreateKeyspace:output_type -> vtctldata.CreateKeyspaceResponse - 89, // 90: vtctlservice.Vtctld.CreateShard:output_type -> vtctldata.CreateShardResponse - 90, // 91: vtctlservice.Vtctld.DeleteCellInfo:output_type -> vtctldata.DeleteCellInfoResponse - 91, // 92: vtctlservice.Vtctld.DeleteCellsAlias:output_type -> vtctldata.DeleteCellsAliasResponse - 92, // 93: vtctlservice.Vtctld.DeleteKeyspace:output_type -> vtctldata.DeleteKeyspaceResponse - 93, // 94: vtctlservice.Vtctld.DeleteShards:output_type -> vtctldata.DeleteShardsResponse - 94, // 95: vtctlservice.Vtctld.DeleteSrvVSchema:output_type -> vtctldata.DeleteSrvVSchemaResponse - 95, // 96: vtctlservice.Vtctld.DeleteTablets:output_type -> vtctldata.DeleteTabletsResponse - 96, // 97: vtctlservice.Vtctld.EmergencyReparentShard:output_type -> vtctldata.EmergencyReparentShardResponse - 97, // 98: vtctlservice.Vtctld.ExecuteFetchAsApp:output_type -> vtctldata.ExecuteFetchAsAppResponse - 98, // 99: vtctlservice.Vtctld.ExecuteFetchAsDBA:output_type -> vtctldata.ExecuteFetchAsDBAResponse - 99, // 100: vtctlservice.Vtctld.ExecuteHook:output_type -> vtctldata.ExecuteHookResponse - 100, // 101: vtctlservice.Vtctld.FindAllShardsInKeyspace:output_type -> vtctldata.FindAllShardsInKeyspaceResponse - 101, // 102: vtctlservice.Vtctld.GetBackups:output_type -> vtctldata.GetBackupsResponse - 102, // 103: vtctlservice.Vtctld.GetCellInfo:output_type -> vtctldata.GetCellInfoResponse - 103, // 104: vtctlservice.Vtctld.GetCellInfoNames:output_type -> vtctldata.GetCellInfoNamesResponse - 104, // 105: vtctlservice.Vtctld.GetCellsAliases:output_type -> vtctldata.GetCellsAliasesResponse - 105, // 106: vtctlservice.Vtctld.GetKeyspace:output_type -> vtctldata.GetKeyspaceResponse - 106, // 107: vtctlservice.Vtctld.GetKeyspaces:output_type -> vtctldata.GetKeyspacesResponse - 107, // 108: vtctlservice.Vtctld.GetPermissions:output_type -> vtctldata.GetPermissionsResponse - 108, // 109: vtctlservice.Vtctld.GetRoutingRules:output_type -> vtctldata.GetRoutingRulesResponse - 109, // 110: vtctlservice.Vtctld.GetSchema:output_type -> vtctldata.GetSchemaResponse - 110, // 111: vtctlservice.Vtctld.GetShard:output_type -> vtctldata.GetShardResponse - 111, // 112: vtctlservice.Vtctld.GetSrvKeyspaceNames:output_type -> vtctldata.GetSrvKeyspaceNamesResponse - 112, // 113: vtctlservice.Vtctld.GetSrvKeyspaces:output_type -> vtctldata.GetSrvKeyspacesResponse - 113, // 114: vtctlservice.Vtctld.GetSrvVSchema:output_type -> vtctldata.GetSrvVSchemaResponse - 114, // 115: vtctlservice.Vtctld.GetSrvVSchemas:output_type -> vtctldata.GetSrvVSchemasResponse - 115, // 116: vtctlservice.Vtctld.GetTablet:output_type -> vtctldata.GetTabletResponse - 116, // 117: vtctlservice.Vtctld.GetTablets:output_type -> vtctldata.GetTabletsResponse - 117, // 118: vtctlservice.Vtctld.GetVersion:output_type -> vtctldata.GetVersionResponse - 118, // 119: vtctlservice.Vtctld.GetVSchema:output_type -> vtctldata.GetVSchemaResponse - 119, // 120: vtctlservice.Vtctld.GetWorkflows:output_type -> vtctldata.GetWorkflowsResponse - 120, // 121: vtctlservice.Vtctld.InitShardPrimary:output_type -> vtctldata.InitShardPrimaryResponse - 121, // 122: vtctlservice.Vtctld.PingTablet:output_type -> vtctldata.PingTabletResponse - 122, // 123: vtctlservice.Vtctld.PlannedReparentShard:output_type -> vtctldata.PlannedReparentShardResponse - 123, // 124: vtctlservice.Vtctld.RebuildKeyspaceGraph:output_type -> vtctldata.RebuildKeyspaceGraphResponse - 124, // 125: vtctlservice.Vtctld.RebuildVSchemaGraph:output_type -> vtctldata.RebuildVSchemaGraphResponse - 125, // 126: vtctlservice.Vtctld.RefreshState:output_type -> vtctldata.RefreshStateResponse - 126, // 127: vtctlservice.Vtctld.RefreshStateByShard:output_type -> vtctldata.RefreshStateByShardResponse - 127, // 128: vtctlservice.Vtctld.ReloadSchema:output_type -> vtctldata.ReloadSchemaResponse - 128, // 129: vtctlservice.Vtctld.ReloadSchemaKeyspace:output_type -> vtctldata.ReloadSchemaKeyspaceResponse - 129, // 130: vtctlservice.Vtctld.ReloadSchemaShard:output_type -> vtctldata.ReloadSchemaShardResponse - 130, // 131: vtctlservice.Vtctld.RemoveBackup:output_type -> vtctldata.RemoveBackupResponse - 131, // 132: vtctlservice.Vtctld.RemoveKeyspaceCell:output_type -> vtctldata.RemoveKeyspaceCellResponse - 132, // 133: vtctlservice.Vtctld.RemoveShardCell:output_type -> vtctldata.RemoveShardCellResponse - 133, // 134: vtctlservice.Vtctld.ReparentTablet:output_type -> vtctldata.ReparentTabletResponse - 134, // 135: vtctlservice.Vtctld.RestoreFromBackup:output_type -> vtctldata.RestoreFromBackupResponse - 135, // 136: vtctlservice.Vtctld.RunHealthCheck:output_type -> vtctldata.RunHealthCheckResponse - 136, // 137: vtctlservice.Vtctld.SetKeyspaceDurabilityPolicy:output_type -> vtctldata.SetKeyspaceDurabilityPolicyResponse - 137, // 138: vtctlservice.Vtctld.SetKeyspaceServedFrom:output_type -> vtctldata.SetKeyspaceServedFromResponse - 138, // 139: vtctlservice.Vtctld.SetShardIsPrimaryServing:output_type -> vtctldata.SetShardIsPrimaryServingResponse - 139, // 140: vtctlservice.Vtctld.SetShardTabletControl:output_type -> vtctldata.SetShardTabletControlResponse - 140, // 141: vtctlservice.Vtctld.SetWritable:output_type -> vtctldata.SetWritableResponse - 141, // 142: vtctlservice.Vtctld.ShardReplicationAdd:output_type -> vtctldata.ShardReplicationAddResponse - 142, // 143: vtctlservice.Vtctld.ShardReplicationFix:output_type -> vtctldata.ShardReplicationFixResponse - 143, // 144: vtctlservice.Vtctld.ShardReplicationPositions:output_type -> vtctldata.ShardReplicationPositionsResponse - 144, // 145: vtctlservice.Vtctld.ShardReplicationRemove:output_type -> vtctldata.ShardReplicationRemoveResponse - 145, // 146: vtctlservice.Vtctld.SleepTablet:output_type -> vtctldata.SleepTabletResponse - 146, // 147: vtctlservice.Vtctld.SourceShardAdd:output_type -> vtctldata.SourceShardAddResponse - 147, // 148: vtctlservice.Vtctld.SourceShardDelete:output_type -> vtctldata.SourceShardDeleteResponse - 148, // 149: vtctlservice.Vtctld.StartReplication:output_type -> vtctldata.StartReplicationResponse - 149, // 150: vtctlservice.Vtctld.StopReplication:output_type -> vtctldata.StopReplicationResponse - 150, // 151: vtctlservice.Vtctld.TabletExternallyReparented:output_type -> vtctldata.TabletExternallyReparentedResponse - 151, // 152: vtctlservice.Vtctld.UpdateCellInfo:output_type -> vtctldata.UpdateCellInfoResponse - 152, // 153: vtctlservice.Vtctld.UpdateCellsAlias:output_type -> vtctldata.UpdateCellsAliasResponse - 153, // 154: vtctlservice.Vtctld.Validate:output_type -> vtctldata.ValidateResponse - 154, // 155: vtctlservice.Vtctld.ValidateKeyspace:output_type -> vtctldata.ValidateKeyspaceResponse - 155, // 156: vtctlservice.Vtctld.ValidateSchemaKeyspace:output_type -> vtctldata.ValidateSchemaKeyspaceResponse - 156, // 157: vtctlservice.Vtctld.ValidateShard:output_type -> vtctldata.ValidateShardResponse - 157, // 158: vtctlservice.Vtctld.ValidateVersionKeyspace:output_type -> vtctldata.ValidateVersionKeyspaceResponse - 158, // 159: vtctlservice.Vtctld.ValidateVSchema:output_type -> vtctldata.ValidateVSchemaResponse - 80, // [80:160] is the sub-list for method output_type - 0, // [0:80] is the sub-list for method input_type + 58, // 58: vtctlservice.Vtctld.SetShardIsPrimaryServing:input_type -> vtctldata.SetShardIsPrimaryServingRequest + 59, // 59: vtctlservice.Vtctld.SetShardTabletControl:input_type -> vtctldata.SetShardTabletControlRequest + 60, // 60: vtctlservice.Vtctld.SetWritable:input_type -> vtctldata.SetWritableRequest + 61, // 61: vtctlservice.Vtctld.ShardReplicationAdd:input_type -> vtctldata.ShardReplicationAddRequest + 62, // 62: vtctlservice.Vtctld.ShardReplicationFix:input_type -> vtctldata.ShardReplicationFixRequest + 63, // 63: vtctlservice.Vtctld.ShardReplicationPositions:input_type -> vtctldata.ShardReplicationPositionsRequest + 64, // 64: vtctlservice.Vtctld.ShardReplicationRemove:input_type -> vtctldata.ShardReplicationRemoveRequest + 65, // 65: vtctlservice.Vtctld.SleepTablet:input_type -> vtctldata.SleepTabletRequest + 66, // 66: vtctlservice.Vtctld.SourceShardAdd:input_type -> vtctldata.SourceShardAddRequest + 67, // 67: vtctlservice.Vtctld.SourceShardDelete:input_type -> vtctldata.SourceShardDeleteRequest + 68, // 68: vtctlservice.Vtctld.StartReplication:input_type -> vtctldata.StartReplicationRequest + 69, // 69: vtctlservice.Vtctld.StopReplication:input_type -> vtctldata.StopReplicationRequest + 70, // 70: vtctlservice.Vtctld.TabletExternallyReparented:input_type -> vtctldata.TabletExternallyReparentedRequest + 71, // 71: vtctlservice.Vtctld.UpdateCellInfo:input_type -> vtctldata.UpdateCellInfoRequest + 72, // 72: vtctlservice.Vtctld.UpdateCellsAlias:input_type -> vtctldata.UpdateCellsAliasRequest + 73, // 73: vtctlservice.Vtctld.Validate:input_type -> vtctldata.ValidateRequest + 74, // 74: vtctlservice.Vtctld.ValidateKeyspace:input_type -> vtctldata.ValidateKeyspaceRequest + 75, // 75: vtctlservice.Vtctld.ValidateSchemaKeyspace:input_type -> vtctldata.ValidateSchemaKeyspaceRequest + 76, // 76: vtctlservice.Vtctld.ValidateShard:input_type -> vtctldata.ValidateShardRequest + 77, // 77: vtctlservice.Vtctld.ValidateVersionKeyspace:input_type -> vtctldata.ValidateVersionKeyspaceRequest + 78, // 78: vtctlservice.Vtctld.ValidateVSchema:input_type -> vtctldata.ValidateVSchemaRequest + 79, // 79: vtctlservice.Vtctl.ExecuteVtctlCommand:output_type -> vtctldata.ExecuteVtctlCommandResponse + 80, // 80: vtctlservice.Vtctld.AddCellInfo:output_type -> vtctldata.AddCellInfoResponse + 81, // 81: vtctlservice.Vtctld.AddCellsAlias:output_type -> vtctldata.AddCellsAliasResponse + 82, // 82: vtctlservice.Vtctld.ApplyRoutingRules:output_type -> vtctldata.ApplyRoutingRulesResponse + 83, // 83: vtctlservice.Vtctld.ApplySchema:output_type -> vtctldata.ApplySchemaResponse + 84, // 84: vtctlservice.Vtctld.ApplyVSchema:output_type -> vtctldata.ApplyVSchemaResponse + 85, // 85: vtctlservice.Vtctld.Backup:output_type -> vtctldata.BackupResponse + 85, // 86: vtctlservice.Vtctld.BackupShard:output_type -> vtctldata.BackupResponse + 86, // 87: vtctlservice.Vtctld.ChangeTabletType:output_type -> vtctldata.ChangeTabletTypeResponse + 87, // 88: vtctlservice.Vtctld.CreateKeyspace:output_type -> vtctldata.CreateKeyspaceResponse + 88, // 89: vtctlservice.Vtctld.CreateShard:output_type -> vtctldata.CreateShardResponse + 89, // 90: vtctlservice.Vtctld.DeleteCellInfo:output_type -> vtctldata.DeleteCellInfoResponse + 90, // 91: vtctlservice.Vtctld.DeleteCellsAlias:output_type -> vtctldata.DeleteCellsAliasResponse + 91, // 92: vtctlservice.Vtctld.DeleteKeyspace:output_type -> vtctldata.DeleteKeyspaceResponse + 92, // 93: vtctlservice.Vtctld.DeleteShards:output_type -> vtctldata.DeleteShardsResponse + 93, // 94: vtctlservice.Vtctld.DeleteSrvVSchema:output_type -> vtctldata.DeleteSrvVSchemaResponse + 94, // 95: vtctlservice.Vtctld.DeleteTablets:output_type -> vtctldata.DeleteTabletsResponse + 95, // 96: vtctlservice.Vtctld.EmergencyReparentShard:output_type -> vtctldata.EmergencyReparentShardResponse + 96, // 97: vtctlservice.Vtctld.ExecuteFetchAsApp:output_type -> vtctldata.ExecuteFetchAsAppResponse + 97, // 98: vtctlservice.Vtctld.ExecuteFetchAsDBA:output_type -> vtctldata.ExecuteFetchAsDBAResponse + 98, // 99: vtctlservice.Vtctld.ExecuteHook:output_type -> vtctldata.ExecuteHookResponse + 99, // 100: vtctlservice.Vtctld.FindAllShardsInKeyspace:output_type -> vtctldata.FindAllShardsInKeyspaceResponse + 100, // 101: vtctlservice.Vtctld.GetBackups:output_type -> vtctldata.GetBackupsResponse + 101, // 102: vtctlservice.Vtctld.GetCellInfo:output_type -> vtctldata.GetCellInfoResponse + 102, // 103: vtctlservice.Vtctld.GetCellInfoNames:output_type -> vtctldata.GetCellInfoNamesResponse + 103, // 104: vtctlservice.Vtctld.GetCellsAliases:output_type -> vtctldata.GetCellsAliasesResponse + 104, // 105: vtctlservice.Vtctld.GetKeyspace:output_type -> vtctldata.GetKeyspaceResponse + 105, // 106: vtctlservice.Vtctld.GetKeyspaces:output_type -> vtctldata.GetKeyspacesResponse + 106, // 107: vtctlservice.Vtctld.GetPermissions:output_type -> vtctldata.GetPermissionsResponse + 107, // 108: vtctlservice.Vtctld.GetRoutingRules:output_type -> vtctldata.GetRoutingRulesResponse + 108, // 109: vtctlservice.Vtctld.GetSchema:output_type -> vtctldata.GetSchemaResponse + 109, // 110: vtctlservice.Vtctld.GetShard:output_type -> vtctldata.GetShardResponse + 110, // 111: vtctlservice.Vtctld.GetSrvKeyspaceNames:output_type -> vtctldata.GetSrvKeyspaceNamesResponse + 111, // 112: vtctlservice.Vtctld.GetSrvKeyspaces:output_type -> vtctldata.GetSrvKeyspacesResponse + 112, // 113: vtctlservice.Vtctld.GetSrvVSchema:output_type -> vtctldata.GetSrvVSchemaResponse + 113, // 114: vtctlservice.Vtctld.GetSrvVSchemas:output_type -> vtctldata.GetSrvVSchemasResponse + 114, // 115: vtctlservice.Vtctld.GetTablet:output_type -> vtctldata.GetTabletResponse + 115, // 116: vtctlservice.Vtctld.GetTablets:output_type -> vtctldata.GetTabletsResponse + 116, // 117: vtctlservice.Vtctld.GetVersion:output_type -> vtctldata.GetVersionResponse + 117, // 118: vtctlservice.Vtctld.GetVSchema:output_type -> vtctldata.GetVSchemaResponse + 118, // 119: vtctlservice.Vtctld.GetWorkflows:output_type -> vtctldata.GetWorkflowsResponse + 119, // 120: vtctlservice.Vtctld.InitShardPrimary:output_type -> vtctldata.InitShardPrimaryResponse + 120, // 121: vtctlservice.Vtctld.PingTablet:output_type -> vtctldata.PingTabletResponse + 121, // 122: vtctlservice.Vtctld.PlannedReparentShard:output_type -> vtctldata.PlannedReparentShardResponse + 122, // 123: vtctlservice.Vtctld.RebuildKeyspaceGraph:output_type -> vtctldata.RebuildKeyspaceGraphResponse + 123, // 124: vtctlservice.Vtctld.RebuildVSchemaGraph:output_type -> vtctldata.RebuildVSchemaGraphResponse + 124, // 125: vtctlservice.Vtctld.RefreshState:output_type -> vtctldata.RefreshStateResponse + 125, // 126: vtctlservice.Vtctld.RefreshStateByShard:output_type -> vtctldata.RefreshStateByShardResponse + 126, // 127: vtctlservice.Vtctld.ReloadSchema:output_type -> vtctldata.ReloadSchemaResponse + 127, // 128: vtctlservice.Vtctld.ReloadSchemaKeyspace:output_type -> vtctldata.ReloadSchemaKeyspaceResponse + 128, // 129: vtctlservice.Vtctld.ReloadSchemaShard:output_type -> vtctldata.ReloadSchemaShardResponse + 129, // 130: vtctlservice.Vtctld.RemoveBackup:output_type -> vtctldata.RemoveBackupResponse + 130, // 131: vtctlservice.Vtctld.RemoveKeyspaceCell:output_type -> vtctldata.RemoveKeyspaceCellResponse + 131, // 132: vtctlservice.Vtctld.RemoveShardCell:output_type -> vtctldata.RemoveShardCellResponse + 132, // 133: vtctlservice.Vtctld.ReparentTablet:output_type -> vtctldata.ReparentTabletResponse + 133, // 134: vtctlservice.Vtctld.RestoreFromBackup:output_type -> vtctldata.RestoreFromBackupResponse + 134, // 135: vtctlservice.Vtctld.RunHealthCheck:output_type -> vtctldata.RunHealthCheckResponse + 135, // 136: vtctlservice.Vtctld.SetKeyspaceDurabilityPolicy:output_type -> vtctldata.SetKeyspaceDurabilityPolicyResponse + 136, // 137: vtctlservice.Vtctld.SetShardIsPrimaryServing:output_type -> vtctldata.SetShardIsPrimaryServingResponse + 137, // 138: vtctlservice.Vtctld.SetShardTabletControl:output_type -> vtctldata.SetShardTabletControlResponse + 138, // 139: vtctlservice.Vtctld.SetWritable:output_type -> vtctldata.SetWritableResponse + 139, // 140: vtctlservice.Vtctld.ShardReplicationAdd:output_type -> vtctldata.ShardReplicationAddResponse + 140, // 141: vtctlservice.Vtctld.ShardReplicationFix:output_type -> vtctldata.ShardReplicationFixResponse + 141, // 142: vtctlservice.Vtctld.ShardReplicationPositions:output_type -> vtctldata.ShardReplicationPositionsResponse + 142, // 143: vtctlservice.Vtctld.ShardReplicationRemove:output_type -> vtctldata.ShardReplicationRemoveResponse + 143, // 144: vtctlservice.Vtctld.SleepTablet:output_type -> vtctldata.SleepTabletResponse + 144, // 145: vtctlservice.Vtctld.SourceShardAdd:output_type -> vtctldata.SourceShardAddResponse + 145, // 146: vtctlservice.Vtctld.SourceShardDelete:output_type -> vtctldata.SourceShardDeleteResponse + 146, // 147: vtctlservice.Vtctld.StartReplication:output_type -> vtctldata.StartReplicationResponse + 147, // 148: vtctlservice.Vtctld.StopReplication:output_type -> vtctldata.StopReplicationResponse + 148, // 149: vtctlservice.Vtctld.TabletExternallyReparented:output_type -> vtctldata.TabletExternallyReparentedResponse + 149, // 150: vtctlservice.Vtctld.UpdateCellInfo:output_type -> vtctldata.UpdateCellInfoResponse + 150, // 151: vtctlservice.Vtctld.UpdateCellsAlias:output_type -> vtctldata.UpdateCellsAliasResponse + 151, // 152: vtctlservice.Vtctld.Validate:output_type -> vtctldata.ValidateResponse + 152, // 153: vtctlservice.Vtctld.ValidateKeyspace:output_type -> vtctldata.ValidateKeyspaceResponse + 153, // 154: vtctlservice.Vtctld.ValidateSchemaKeyspace:output_type -> vtctldata.ValidateSchemaKeyspaceResponse + 154, // 155: vtctlservice.Vtctld.ValidateShard:output_type -> vtctldata.ValidateShardResponse + 155, // 156: vtctlservice.Vtctld.ValidateVersionKeyspace:output_type -> vtctldata.ValidateVersionKeyspaceResponse + 156, // 157: vtctlservice.Vtctld.ValidateVSchema:output_type -> vtctldata.ValidateVSchemaResponse + 79, // [79:158] is the sub-list for method output_type + 0, // [0:79] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name diff --git a/go/vt/proto/vtctlservice/vtctlservice_grpc.pb.go b/go/vt/proto/vtctlservice/vtctlservice_grpc.pb.go index 8b49351bef2..af58e5452a1 100644 --- a/go/vt/proto/vtctlservice/vtctlservice_grpc.pb.go +++ b/go/vt/proto/vtctlservice/vtctlservice_grpc.pb.go @@ -304,11 +304,6 @@ type VtctldClient interface { RunHealthCheck(ctx context.Context, in *vtctldata.RunHealthCheckRequest, opts ...grpc.CallOption) (*vtctldata.RunHealthCheckResponse, error) // SetKeyspaceDurabilityPolicy updates the DurabilityPolicy for a keyspace. SetKeyspaceDurabilityPolicy(ctx context.Context, in *vtctldata.SetKeyspaceDurabilityPolicyRequest, opts ...grpc.CallOption) (*vtctldata.SetKeyspaceDurabilityPolicyResponse, error) - // SetKeyspaceServedFrom changes the ServedFromMap manually, and is intended - // only for emergency fixes. This does not rebuild the serving graph. - // - // The ServedFromMap is automatically updated as a part of MigrateServedFrom. - SetKeyspaceServedFrom(ctx context.Context, in *vtctldata.SetKeyspaceServedFromRequest, opts ...grpc.CallOption) (*vtctldata.SetKeyspaceServedFromResponse, error) // SetShardIsPrimaryServing adds or removes a shard from serving. // // This is meant as an emergency function. It does not rebuild any serving @@ -983,15 +978,6 @@ func (c *vtctldClient) SetKeyspaceDurabilityPolicy(ctx context.Context, in *vtct return out, nil } -func (c *vtctldClient) SetKeyspaceServedFrom(ctx context.Context, in *vtctldata.SetKeyspaceServedFromRequest, opts ...grpc.CallOption) (*vtctldata.SetKeyspaceServedFromResponse, error) { - out := new(vtctldata.SetKeyspaceServedFromResponse) - err := c.cc.Invoke(ctx, "/vtctlservice.Vtctld/SetKeyspaceServedFrom", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - func (c *vtctldClient) SetShardIsPrimaryServing(ctx context.Context, in *vtctldata.SetShardIsPrimaryServingRequest, opts ...grpc.CallOption) (*vtctldata.SetShardIsPrimaryServingResponse, error) { out := new(vtctldata.SetShardIsPrimaryServingResponse) err := c.cc.Invoke(ctx, "/vtctlservice.Vtctld/SetShardIsPrimaryServing", in, out, opts...) @@ -1353,11 +1339,6 @@ type VtctldServer interface { RunHealthCheck(context.Context, *vtctldata.RunHealthCheckRequest) (*vtctldata.RunHealthCheckResponse, error) // SetKeyspaceDurabilityPolicy updates the DurabilityPolicy for a keyspace. SetKeyspaceDurabilityPolicy(context.Context, *vtctldata.SetKeyspaceDurabilityPolicyRequest) (*vtctldata.SetKeyspaceDurabilityPolicyResponse, error) - // SetKeyspaceServedFrom changes the ServedFromMap manually, and is intended - // only for emergency fixes. This does not rebuild the serving graph. - // - // The ServedFromMap is automatically updated as a part of MigrateServedFrom. - SetKeyspaceServedFrom(context.Context, *vtctldata.SetKeyspaceServedFromRequest) (*vtctldata.SetKeyspaceServedFromResponse, error) // SetShardIsPrimaryServing adds or removes a shard from serving. // // This is meant as an emergency function. It does not rebuild any serving @@ -1618,9 +1599,6 @@ func (UnimplementedVtctldServer) RunHealthCheck(context.Context, *vtctldata.RunH func (UnimplementedVtctldServer) SetKeyspaceDurabilityPolicy(context.Context, *vtctldata.SetKeyspaceDurabilityPolicyRequest) (*vtctldata.SetKeyspaceDurabilityPolicyResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method SetKeyspaceDurabilityPolicy not implemented") } -func (UnimplementedVtctldServer) SetKeyspaceServedFrom(context.Context, *vtctldata.SetKeyspaceServedFromRequest) (*vtctldata.SetKeyspaceServedFromResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method SetKeyspaceServedFrom not implemented") -} func (UnimplementedVtctldServer) SetShardIsPrimaryServing(context.Context, *vtctldata.SetShardIsPrimaryServingRequest) (*vtctldata.SetShardIsPrimaryServingResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method SetShardIsPrimaryServing not implemented") } @@ -2732,24 +2710,6 @@ func _Vtctld_SetKeyspaceDurabilityPolicy_Handler(srv interface{}, ctx context.Co return interceptor(ctx, in, info, handler) } -func _Vtctld_SetKeyspaceServedFrom_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(vtctldata.SetKeyspaceServedFromRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(VtctldServer).SetKeyspaceServedFrom(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/vtctlservice.Vtctld/SetKeyspaceServedFrom", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(VtctldServer).SetKeyspaceServedFrom(ctx, req.(*vtctldata.SetKeyspaceServedFromRequest)) - } - return interceptor(ctx, in, info, handler) -} - func _Vtctld_SetShardIsPrimaryServing_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(vtctldata.SetShardIsPrimaryServingRequest) if err := dec(in); err != nil { @@ -3351,10 +3311,6 @@ var Vtctld_ServiceDesc = grpc.ServiceDesc{ MethodName: "SetKeyspaceDurabilityPolicy", Handler: _Vtctld_SetKeyspaceDurabilityPolicy_Handler, }, - { - MethodName: "SetKeyspaceServedFrom", - Handler: _Vtctld_SetKeyspaceServedFrom_Handler, - }, { MethodName: "SetShardIsPrimaryServing", Handler: _Vtctld_SetShardIsPrimaryServing_Handler, diff --git a/go/vt/proto/vtworkerdata/vtworkerdata.pb.go b/go/vt/proto/vtworkerdata/vtworkerdata.pb.go deleted file mode 100644 index 48327428211..00000000000 --- a/go/vt/proto/vtworkerdata/vtworkerdata.pb.go +++ /dev/null @@ -1,233 +0,0 @@ -// -//Copyright 2019 The Vitess Authors. -// -//Licensed under the Apache License, Version 2.0 (the "License"); -//you may not use this file except in compliance with the License. -//You may obtain a copy of the License at -// -//http://www.apache.org/licenses/LICENSE-2.0 -// -//Unless required by applicable law or agreed to in writing, software -//distributed under the License is distributed on an "AS IS" BASIS, -//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -//See the License for the specific language governing permissions and -//limitations under the License. - -// Data structures for the vtworker RPC interface. - -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.28.0 -// protoc v3.19.4 -// source: vtworkerdata.proto - -package vtworkerdata - -import ( - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" - sync "sync" - logutil "vitess.io/vitess/go/vt/proto/logutil" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -// ExecuteVtworkerCommandRequest is the payload for ExecuteVtworkerCommand. -type ExecuteVtworkerCommandRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Args []string `protobuf:"bytes,1,rep,name=args,proto3" json:"args,omitempty"` -} - -func (x *ExecuteVtworkerCommandRequest) Reset() { - *x = ExecuteVtworkerCommandRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_vtworkerdata_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ExecuteVtworkerCommandRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ExecuteVtworkerCommandRequest) ProtoMessage() {} - -func (x *ExecuteVtworkerCommandRequest) ProtoReflect() protoreflect.Message { - mi := &file_vtworkerdata_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ExecuteVtworkerCommandRequest.ProtoReflect.Descriptor instead. -func (*ExecuteVtworkerCommandRequest) Descriptor() ([]byte, []int) { - return file_vtworkerdata_proto_rawDescGZIP(), []int{0} -} - -func (x *ExecuteVtworkerCommandRequest) GetArgs() []string { - if x != nil { - return x.Args - } - return nil -} - -// ExecuteVtworkerCommandResponse is streamed back by ExecuteVtworkerCommand. -type ExecuteVtworkerCommandResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Event *logutil.Event `protobuf:"bytes,1,opt,name=event,proto3" json:"event,omitempty"` -} - -func (x *ExecuteVtworkerCommandResponse) Reset() { - *x = ExecuteVtworkerCommandResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_vtworkerdata_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ExecuteVtworkerCommandResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ExecuteVtworkerCommandResponse) ProtoMessage() {} - -func (x *ExecuteVtworkerCommandResponse) ProtoReflect() protoreflect.Message { - mi := &file_vtworkerdata_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ExecuteVtworkerCommandResponse.ProtoReflect.Descriptor instead. -func (*ExecuteVtworkerCommandResponse) Descriptor() ([]byte, []int) { - return file_vtworkerdata_proto_rawDescGZIP(), []int{1} -} - -func (x *ExecuteVtworkerCommandResponse) GetEvent() *logutil.Event { - if x != nil { - return x.Event - } - return nil -} - -var File_vtworkerdata_proto protoreflect.FileDescriptor - -var file_vtworkerdata_proto_rawDesc = []byte{ - 0x0a, 0x12, 0x76, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0c, 0x76, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x64, 0x61, - 0x74, 0x61, 0x1a, 0x0d, 0x6c, 0x6f, 0x67, 0x75, 0x74, 0x69, 0x6c, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x22, 0x33, 0x0a, 0x1d, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x56, 0x74, 0x77, 0x6f, - 0x72, 0x6b, 0x65, 0x72, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x72, 0x67, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, - 0x52, 0x04, 0x61, 0x72, 0x67, 0x73, 0x22, 0x46, 0x0a, 0x1e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, - 0x65, 0x56, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x24, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, - 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6c, 0x6f, 0x67, 0x75, 0x74, 0x69, - 0x6c, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x42, 0x2b, - 0x5a, 0x29, 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, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x64, 0x61, 0x74, 0x61, 0x62, 0x06, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x33, -} - -var ( - file_vtworkerdata_proto_rawDescOnce sync.Once - file_vtworkerdata_proto_rawDescData = file_vtworkerdata_proto_rawDesc -) - -func file_vtworkerdata_proto_rawDescGZIP() []byte { - file_vtworkerdata_proto_rawDescOnce.Do(func() { - file_vtworkerdata_proto_rawDescData = protoimpl.X.CompressGZIP(file_vtworkerdata_proto_rawDescData) - }) - return file_vtworkerdata_proto_rawDescData -} - -var file_vtworkerdata_proto_msgTypes = make([]protoimpl.MessageInfo, 2) -var file_vtworkerdata_proto_goTypes = []interface{}{ - (*ExecuteVtworkerCommandRequest)(nil), // 0: vtworkerdata.ExecuteVtworkerCommandRequest - (*ExecuteVtworkerCommandResponse)(nil), // 1: vtworkerdata.ExecuteVtworkerCommandResponse - (*logutil.Event)(nil), // 2: logutil.Event -} -var file_vtworkerdata_proto_depIdxs = []int32{ - 2, // 0: vtworkerdata.ExecuteVtworkerCommandResponse.event:type_name -> logutil.Event - 1, // [1:1] is the sub-list for method output_type - 1, // [1:1] is the sub-list for method input_type - 1, // [1:1] is the sub-list for extension type_name - 1, // [1:1] is the sub-list for extension extendee - 0, // [0:1] is the sub-list for field type_name -} - -func init() { file_vtworkerdata_proto_init() } -func file_vtworkerdata_proto_init() { - if File_vtworkerdata_proto != nil { - return - } - if !protoimpl.UnsafeEnabled { - file_vtworkerdata_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ExecuteVtworkerCommandRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_vtworkerdata_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ExecuteVtworkerCommandResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_vtworkerdata_proto_rawDesc, - NumEnums: 0, - NumMessages: 2, - NumExtensions: 0, - NumServices: 0, - }, - GoTypes: file_vtworkerdata_proto_goTypes, - DependencyIndexes: file_vtworkerdata_proto_depIdxs, - MessageInfos: file_vtworkerdata_proto_msgTypes, - }.Build() - File_vtworkerdata_proto = out.File - file_vtworkerdata_proto_rawDesc = nil - file_vtworkerdata_proto_goTypes = nil - file_vtworkerdata_proto_depIdxs = nil -} diff --git a/go/vt/proto/vtworkerdata/vtworkerdata_vtproto.pb.go b/go/vt/proto/vtworkerdata/vtworkerdata_vtproto.pb.go deleted file mode 100644 index 90dc844e39c..00000000000 --- a/go/vt/proto/vtworkerdata/vtworkerdata_vtproto.pb.go +++ /dev/null @@ -1,411 +0,0 @@ -// Code generated by protoc-gen-go-vtproto. DO NOT EDIT. -// protoc-gen-go-vtproto version: v0.3.0 -// source: vtworkerdata.proto - -package vtworkerdata - -import ( - fmt "fmt" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - io "io" - bits "math/bits" - logutil "vitess.io/vitess/go/vt/proto/logutil" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -func (m *ExecuteVtworkerCommandRequest) MarshalVT() (dAtA []byte, err error) { - if m == nil { - return nil, nil - } - size := m.SizeVT() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBufferVT(dAtA[:size]) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *ExecuteVtworkerCommandRequest) MarshalToVT(dAtA []byte) (int, error) { - size := m.SizeVT() - return m.MarshalToSizedBufferVT(dAtA[:size]) -} - -func (m *ExecuteVtworkerCommandRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { - if m == nil { - return 0, nil - } - i := len(dAtA) - _ = i - var l int - _ = l - if m.unknownFields != nil { - i -= len(m.unknownFields) - copy(dAtA[i:], m.unknownFields) - } - if len(m.Args) > 0 { - for iNdEx := len(m.Args) - 1; iNdEx >= 0; iNdEx-- { - i -= len(m.Args[iNdEx]) - copy(dAtA[i:], m.Args[iNdEx]) - i = encodeVarint(dAtA, i, uint64(len(m.Args[iNdEx]))) - i-- - dAtA[i] = 0xa - } - } - return len(dAtA) - i, nil -} - -func (m *ExecuteVtworkerCommandResponse) MarshalVT() (dAtA []byte, err error) { - if m == nil { - return nil, nil - } - size := m.SizeVT() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBufferVT(dAtA[:size]) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *ExecuteVtworkerCommandResponse) MarshalToVT(dAtA []byte) (int, error) { - size := m.SizeVT() - return m.MarshalToSizedBufferVT(dAtA[:size]) -} - -func (m *ExecuteVtworkerCommandResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { - if m == nil { - return 0, nil - } - i := len(dAtA) - _ = i - var l int - _ = l - if m.unknownFields != nil { - i -= len(m.unknownFields) - copy(dAtA[i:], m.unknownFields) - } - if m.Event != nil { - size, err := m.Event.MarshalToSizedBufferVT(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarint(dAtA, i, uint64(size)) - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil -} - -func encodeVarint(dAtA []byte, offset int, v uint64) int { - offset -= sov(v) - base := offset - for v >= 1<<7 { - dAtA[offset] = uint8(v&0x7f | 0x80) - v >>= 7 - offset++ - } - dAtA[offset] = uint8(v) - return base -} -func (m *ExecuteVtworkerCommandRequest) SizeVT() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if len(m.Args) > 0 { - for _, s := range m.Args { - l = len(s) - n += 1 + l + sov(uint64(l)) - } - } - if m.unknownFields != nil { - n += len(m.unknownFields) - } - return n -} - -func (m *ExecuteVtworkerCommandResponse) SizeVT() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.Event != nil { - l = m.Event.SizeVT() - n += 1 + l + sov(uint64(l)) - } - if m.unknownFields != nil { - n += len(m.unknownFields) - } - return n -} - -func sov(x uint64) (n int) { - return (bits.Len64(x|1) + 6) / 7 -} -func soz(x uint64) (n int) { - return sov(uint64((x << 1) ^ uint64((int64(x) >> 63)))) -} -func (m *ExecuteVtworkerCommandRequest) UnmarshalVT(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflow - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: ExecuteVtworkerCommandRequest: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: ExecuteVtworkerCommandRequest: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Args", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 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 ErrInvalidLength - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLength - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Args = append(m.Args, string(dAtA[iNdEx:postIndex])) - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skip(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLength - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *ExecuteVtworkerCommandResponse) UnmarshalVT(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflow - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: ExecuteVtworkerCommandResponse: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: ExecuteVtworkerCommandResponse: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Event", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflow - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLength - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLength - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.Event == nil { - m.Event = &logutil.Event{} - } - if err := m.Event.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skip(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLength - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func skip(dAtA []byte) (n int, err error) { - l := len(dAtA) - iNdEx := 0 - depth := 0 - for iNdEx < l { - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflow - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - wireType := int(wire & 0x7) - switch wireType { - case 0: - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflow - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - iNdEx++ - if dAtA[iNdEx-1] < 0x80 { - break - } - } - case 1: - iNdEx += 8 - case 2: - var length int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflow - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - length |= (int(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - if length < 0 { - return 0, ErrInvalidLength - } - iNdEx += length - case 3: - depth++ - case 4: - if depth == 0 { - return 0, ErrUnexpectedEndOfGroup - } - depth-- - case 5: - iNdEx += 4 - default: - return 0, fmt.Errorf("proto: illegal wireType %d", wireType) - } - if iNdEx < 0 { - return 0, ErrInvalidLength - } - if depth == 0 { - return iNdEx, nil - } - } - return 0, io.ErrUnexpectedEOF -} - -var ( - ErrInvalidLength = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflow = fmt.Errorf("proto: integer overflow") - ErrUnexpectedEndOfGroup = fmt.Errorf("proto: unexpected end of group") -) diff --git a/go/vt/proto/vtworkerservice/vtworkerservice.pb.go b/go/vt/proto/vtworkerservice/vtworkerservice.pb.go deleted file mode 100644 index 16eb81f39bd..00000000000 --- a/go/vt/proto/vtworkerservice/vtworkerservice.pb.go +++ /dev/null @@ -1,98 +0,0 @@ -// -//Copyright 2019 The Vitess Authors. -// -//Licensed under the Apache License, Version 2.0 (the "License"); -//you may not use this file except in compliance with the License. -//You may obtain a copy of the License at -// -//http://www.apache.org/licenses/LICENSE-2.0 -// -//Unless required by applicable law or agreed to in writing, software -//distributed under the License is distributed on an "AS IS" BASIS, -//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -//See the License for the specific language governing permissions and -//limitations under the License. - -// RPC interface for vtworker. -// The interface is very similar to the vtctld interface (see vtctlservice.proto). - -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.28.0 -// protoc v3.19.4 -// source: vtworkerservice.proto - -package vtworkerservice - -import ( - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" - vtworkerdata "vitess.io/vitess/go/vt/proto/vtworkerdata" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -var File_vtworkerservice_proto protoreflect.FileDescriptor - -var file_vtworkerservice_proto_rawDesc = []byte{ - 0x0a, 0x15, 0x76, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0f, 0x76, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x65, - 0x72, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x1a, 0x12, 0x76, 0x74, 0x77, 0x6f, 0x72, 0x6b, - 0x65, 0x72, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x32, 0x83, 0x01, 0x0a, - 0x08, 0x56, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x12, 0x77, 0x0a, 0x16, 0x45, 0x78, 0x65, - 0x63, 0x75, 0x74, 0x65, 0x56, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x43, 0x6f, 0x6d, 0x6d, - 0x61, 0x6e, 0x64, 0x12, 0x2b, 0x2e, 0x76, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x64, 0x61, - 0x74, 0x61, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x56, 0x74, 0x77, 0x6f, 0x72, 0x6b, - 0x65, 0x72, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x2c, 0x2e, 0x76, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x64, 0x61, 0x74, 0x61, 0x2e, - 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x56, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x43, - 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, - 0x30, 0x01, 0x42, 0x2e, 0x5a, 0x2c, 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, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x65, 0x72, 0x76, 0x69, - 0x63, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, -} - -var file_vtworkerservice_proto_goTypes = []interface{}{ - (*vtworkerdata.ExecuteVtworkerCommandRequest)(nil), // 0: vtworkerdata.ExecuteVtworkerCommandRequest - (*vtworkerdata.ExecuteVtworkerCommandResponse)(nil), // 1: vtworkerdata.ExecuteVtworkerCommandResponse -} -var file_vtworkerservice_proto_depIdxs = []int32{ - 0, // 0: vtworkerservice.Vtworker.ExecuteVtworkerCommand:input_type -> vtworkerdata.ExecuteVtworkerCommandRequest - 1, // 1: vtworkerservice.Vtworker.ExecuteVtworkerCommand:output_type -> vtworkerdata.ExecuteVtworkerCommandResponse - 1, // [1:2] is the sub-list for method output_type - 0, // [0:1] is the sub-list for method input_type - 0, // [0:0] is the sub-list for extension type_name - 0, // [0:0] is the sub-list for extension extendee - 0, // [0:0] is the sub-list for field type_name -} - -func init() { file_vtworkerservice_proto_init() } -func file_vtworkerservice_proto_init() { - if File_vtworkerservice_proto != nil { - return - } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_vtworkerservice_proto_rawDesc, - NumEnums: 0, - NumMessages: 0, - NumExtensions: 0, - NumServices: 1, - }, - GoTypes: file_vtworkerservice_proto_goTypes, - DependencyIndexes: file_vtworkerservice_proto_depIdxs, - }.Build() - File_vtworkerservice_proto = out.File - file_vtworkerservice_proto_rawDesc = nil - file_vtworkerservice_proto_goTypes = nil - file_vtworkerservice_proto_depIdxs = nil -} diff --git a/go/vt/proto/vtworkerservice/vtworkerservice_grpc.pb.go b/go/vt/proto/vtworkerservice/vtworkerservice_grpc.pb.go deleted file mode 100644 index d1936927fc3..00000000000 --- a/go/vt/proto/vtworkerservice/vtworkerservice_grpc.pb.go +++ /dev/null @@ -1,137 +0,0 @@ -// Code generated by protoc-gen-go-grpc. DO NOT EDIT. -// versions: -// - protoc-gen-go-grpc v1.2.0 -// - protoc v3.19.4 -// source: vtworkerservice.proto - -package vtworkerservice - -import ( - context "context" - grpc "google.golang.org/grpc" - codes "google.golang.org/grpc/codes" - status "google.golang.org/grpc/status" - vtworkerdata "vitess.io/vitess/go/vt/proto/vtworkerdata" -) - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.32.0 or later. -const _ = grpc.SupportPackageIsVersion7 - -// VtworkerClient is the client API for Vtworker service. -// -// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. -type VtworkerClient interface { - // ExecuteVtworkerCommand allows to run a vtworker command by specifying the - // same arguments as on the command line. - ExecuteVtworkerCommand(ctx context.Context, in *vtworkerdata.ExecuteVtworkerCommandRequest, opts ...grpc.CallOption) (Vtworker_ExecuteVtworkerCommandClient, error) -} - -type vtworkerClient struct { - cc grpc.ClientConnInterface -} - -func NewVtworkerClient(cc grpc.ClientConnInterface) VtworkerClient { - return &vtworkerClient{cc} -} - -func (c *vtworkerClient) ExecuteVtworkerCommand(ctx context.Context, in *vtworkerdata.ExecuteVtworkerCommandRequest, opts ...grpc.CallOption) (Vtworker_ExecuteVtworkerCommandClient, error) { - stream, err := c.cc.NewStream(ctx, &Vtworker_ServiceDesc.Streams[0], "/vtworkerservice.Vtworker/ExecuteVtworkerCommand", opts...) - if err != nil { - return nil, err - } - x := &vtworkerExecuteVtworkerCommandClient{stream} - if err := x.ClientStream.SendMsg(in); err != nil { - return nil, err - } - if err := x.ClientStream.CloseSend(); err != nil { - return nil, err - } - return x, nil -} - -type Vtworker_ExecuteVtworkerCommandClient interface { - Recv() (*vtworkerdata.ExecuteVtworkerCommandResponse, error) - grpc.ClientStream -} - -type vtworkerExecuteVtworkerCommandClient struct { - grpc.ClientStream -} - -func (x *vtworkerExecuteVtworkerCommandClient) Recv() (*vtworkerdata.ExecuteVtworkerCommandResponse, error) { - m := new(vtworkerdata.ExecuteVtworkerCommandResponse) - if err := x.ClientStream.RecvMsg(m); err != nil { - return nil, err - } - return m, nil -} - -// VtworkerServer is the server API for Vtworker service. -// All implementations must embed UnimplementedVtworkerServer -// for forward compatibility -type VtworkerServer interface { - // ExecuteVtworkerCommand allows to run a vtworker command by specifying the - // same arguments as on the command line. - ExecuteVtworkerCommand(*vtworkerdata.ExecuteVtworkerCommandRequest, Vtworker_ExecuteVtworkerCommandServer) error - mustEmbedUnimplementedVtworkerServer() -} - -// UnimplementedVtworkerServer must be embedded to have forward compatible implementations. -type UnimplementedVtworkerServer struct { -} - -func (UnimplementedVtworkerServer) ExecuteVtworkerCommand(*vtworkerdata.ExecuteVtworkerCommandRequest, Vtworker_ExecuteVtworkerCommandServer) error { - return status.Errorf(codes.Unimplemented, "method ExecuteVtworkerCommand not implemented") -} -func (UnimplementedVtworkerServer) mustEmbedUnimplementedVtworkerServer() {} - -// UnsafeVtworkerServer may be embedded to opt out of forward compatibility for this service. -// Use of this interface is not recommended, as added methods to VtworkerServer will -// result in compilation errors. -type UnsafeVtworkerServer interface { - mustEmbedUnimplementedVtworkerServer() -} - -func RegisterVtworkerServer(s grpc.ServiceRegistrar, srv VtworkerServer) { - s.RegisterService(&Vtworker_ServiceDesc, srv) -} - -func _Vtworker_ExecuteVtworkerCommand_Handler(srv interface{}, stream grpc.ServerStream) error { - m := new(vtworkerdata.ExecuteVtworkerCommandRequest) - if err := stream.RecvMsg(m); err != nil { - return err - } - return srv.(VtworkerServer).ExecuteVtworkerCommand(m, &vtworkerExecuteVtworkerCommandServer{stream}) -} - -type Vtworker_ExecuteVtworkerCommandServer interface { - Send(*vtworkerdata.ExecuteVtworkerCommandResponse) error - grpc.ServerStream -} - -type vtworkerExecuteVtworkerCommandServer struct { - grpc.ServerStream -} - -func (x *vtworkerExecuteVtworkerCommandServer) Send(m *vtworkerdata.ExecuteVtworkerCommandResponse) error { - return x.ServerStream.SendMsg(m) -} - -// Vtworker_ServiceDesc is the grpc.ServiceDesc for Vtworker service. -// It's only intended for direct use with grpc.RegisterService, -// and not to be introspected or modified (even as a copy) -var Vtworker_ServiceDesc = grpc.ServiceDesc{ - ServiceName: "vtworkerservice.Vtworker", - HandlerType: (*VtworkerServer)(nil), - Methods: []grpc.MethodDesc{}, - Streams: []grpc.StreamDesc{ - { - StreamName: "ExecuteVtworkerCommand", - Handler: _Vtworker_ExecuteVtworkerCommand_Handler, - ServerStreams: true, - }, - }, - Metadata: "vtworkerservice.proto", -} diff --git a/go/vt/servenv/service_map.go b/go/vt/servenv/service_map.go index 15c219ef555..4616f058b99 100644 --- a/go/vt/servenv/service_map.go +++ b/go/vt/servenv/service_map.go @@ -34,7 +34,7 @@ var ( ) func init() { - flag.Var(&serviceMapFlag, "service_map", "comma separated list of services to enable (or disable if prefixed with '-') Example: grpc-vtworker") + flag.Var(&serviceMapFlag, "service_map", "comma separated list of services to enable (or disable if prefixed with '-') Example: grpc-queryservice") OnInit(func() { updateServiceMap() }) diff --git a/go/vt/topotools/events/migrate.go b/go/vt/topotools/events/migrate.go deleted file mode 100644 index d91f211c222..00000000000 --- a/go/vt/topotools/events/migrate.go +++ /dev/null @@ -1,49 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package events - -import ( - base "vitess.io/vitess/go/vt/events" - "vitess.io/vitess/go/vt/topo" - - topodatapb "vitess.io/vitess/go/vt/proto/topodata" -) - -// MigrateServedFrom is an event that describes a single step in the process of -// adding or removing a forwarding rule to have certain ServedTypes served by -// another keyspace. -type MigrateServedFrom struct { - base.StatusUpdater - - KeyspaceName string - SourceShard topo.ShardInfo - DestinationShard topo.ShardInfo - ServedType topodatapb.TabletType - Reverse bool -} - -// MigrateServedTypes is an event that describes a single step in the process of -// switching a ServedType from one set of shards to another. -type MigrateServedTypes struct { - base.StatusUpdater - - KeyspaceName string - SourceShards []*topo.ShardInfo - DestinationShards []*topo.ShardInfo - ServedType topodatapb.TabletType - Reverse bool -} diff --git a/go/vt/topotools/events/migrate_syslog.go b/go/vt/topotools/events/migrate_syslog.go deleted file mode 100644 index ae1da2d2006..00000000000 --- a/go/vt/topotools/events/migrate_syslog.go +++ /dev/null @@ -1,73 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package events - -import ( - "fmt" - "log/syslog" - "strings" - - "vitess.io/vitess/go/event/syslogger" -) - -// Syslog writes a MigrateServedFrom event to syslog. -func (ev *MigrateServedFrom) Syslog() (syslog.Priority, string) { - var format string - if ev.Reverse { - format = "%s [migrate served-from %s/%s <- %s/%s] %s" - } else { - format = "%s [migrate served-from %s/%s -> %s/%s] %s" - } - return syslog.LOG_INFO, fmt.Sprintf(format, - ev.KeyspaceName, - ev.SourceShard.Keyspace(), ev.SourceShard.ShardName(), - ev.DestinationShard.Keyspace(), ev.DestinationShard.ShardName(), - ev.Status) -} - -var _ syslogger.Syslogger = (*MigrateServedFrom)(nil) // compile-time interface check - -// Syslog writes a MigrateServedTypes event to syslog. -func (ev *MigrateServedTypes) Syslog() (syslog.Priority, string) { - var format string - if ev.Reverse { - format = "%s [migrate served-types {%v} <- {%v}] %s" - } else { - format = "%s [migrate served-types {%v} -> {%v}] %s" - } - - sourceShards := make([]string, len(ev.SourceShards)) - for i, shard := range ev.SourceShards { - if shard == nil { - continue - } - sourceShards[i] = shard.ShardName() - } - destShards := make([]string, len(ev.DestinationShards)) - for i, shard := range ev.DestinationShards { - if shard == nil { - continue - } - destShards[i] = shard.ShardName() - } - - return syslog.LOG_INFO, fmt.Sprintf(format, - ev.KeyspaceName, strings.Join(sourceShards, ", "), - strings.Join(destShards, ", "), ev.Status) -} - -var _ syslogger.Syslogger = (*MigrateServedTypes)(nil) // compile-time interface check diff --git a/go/vt/topotools/events/migrate_syslog_test.go b/go/vt/topotools/events/migrate_syslog_test.go deleted file mode 100644 index 01e25415d28..00000000000 --- a/go/vt/topotools/events/migrate_syslog_test.go +++ /dev/null @@ -1,138 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package events - -import ( - "log/syslog" - "testing" - - base "vitess.io/vitess/go/vt/events" - "vitess.io/vitess/go/vt/topo" -) - -func TestMigrateServedFromSyslogForward(t *testing.T) { - wantSev, wantMsg := syslog.LOG_INFO, "keyspace-1 [migrate served-from keyspace-2/source-shard -> keyspace-1/dest-shard] status" - ev := &MigrateServedFrom{ - KeyspaceName: "keyspace-1", - SourceShard: *topo.NewShardInfo("keyspace-2", "source-shard", nil, nil), - DestinationShard: *topo.NewShardInfo("keyspace-1", "dest-shard", nil, nil), - Reverse: false, - StatusUpdater: base.StatusUpdater{Status: "status"}, - } - gotSev, gotMsg := ev.Syslog() - - if gotSev != wantSev { - t.Errorf("wrong severity: got %v, want %v", gotSev, wantSev) - } - if gotMsg != wantMsg { - t.Errorf("wrong message: got %v, want %v", gotMsg, wantMsg) - } -} - -func TestMigrateServedFromSyslogReverse(t *testing.T) { - wantSev, wantMsg := syslog.LOG_INFO, "keyspace-1 [migrate served-from keyspace-2/source-shard <- keyspace-1/dest-shard] status" - ev := &MigrateServedFrom{ - KeyspaceName: "keyspace-1", - SourceShard: *topo.NewShardInfo("keyspace-2", "source-shard", nil, nil), - DestinationShard: *topo.NewShardInfo("keyspace-1", "dest-shard", nil, nil), - Reverse: true, - StatusUpdater: base.StatusUpdater{Status: "status"}, - } - gotSev, gotMsg := ev.Syslog() - - if gotSev != wantSev { - t.Errorf("wrong severity: got %v, want %v", gotSev, wantSev) - } - if gotMsg != wantMsg { - t.Errorf("wrong message: got %v, want %v", gotMsg, wantMsg) - } -} - -func TestMigrateServedTypesSyslogForward(t *testing.T) { - wantSev, wantMsg := syslog.LOG_INFO, "keyspace-1 [migrate served-types {src1, src2} -> {dst1, dst2}] status" - ev := &MigrateServedTypes{ - KeyspaceName: "keyspace-1", - SourceShards: []*topo.ShardInfo{ - topo.NewShardInfo("keyspace-1", "src1", nil, nil), - topo.NewShardInfo("keyspace-1", "src2", nil, nil), - }, - DestinationShards: []*topo.ShardInfo{ - topo.NewShardInfo("keyspace-1", "dst1", nil, nil), - topo.NewShardInfo("keyspace-1", "dst2", nil, nil), - }, - Reverse: false, - StatusUpdater: base.StatusUpdater{Status: "status"}, - } - gotSev, gotMsg := ev.Syslog() - - if gotSev != wantSev { - t.Errorf("wrong severity: got %v, want %v", gotSev, wantSev) - } - if gotMsg != wantMsg { - t.Errorf("wrong message: got %v, want %v", gotMsg, wantMsg) - } -} - -func TestMigrateServedTypesSyslogForwardWithNil(t *testing.T) { - wantSev, wantMsg := syslog.LOG_INFO, "keyspace-1 [migrate served-types {src1, } -> {, dst2}] status" - ev := &MigrateServedTypes{ - KeyspaceName: "keyspace-1", - SourceShards: []*topo.ShardInfo{ - topo.NewShardInfo("keyspace-1", "src1", nil, nil), - nil, - }, - DestinationShards: []*topo.ShardInfo{ - nil, - topo.NewShardInfo("keyspace-1", "dst2", nil, nil), - }, - Reverse: false, - StatusUpdater: base.StatusUpdater{Status: "status"}, - } - gotSev, gotMsg := ev.Syslog() - - if gotSev != wantSev { - t.Errorf("wrong severity: got %v, want %v", gotSev, wantSev) - } - if gotMsg != wantMsg { - t.Errorf("wrong message: got %v, want %v", gotMsg, wantMsg) - } -} - -func TestMigrateServedTypesSyslogReverse(t *testing.T) { - wantSev, wantMsg := syslog.LOG_INFO, "keyspace-1 [migrate served-types {src1, src2} <- {dst1, dst2}] status" - ev := &MigrateServedTypes{ - KeyspaceName: "keyspace-1", - SourceShards: []*topo.ShardInfo{ - topo.NewShardInfo("keyspace-1", "src1", nil, nil), - topo.NewShardInfo("keyspace-1", "src2", nil, nil), - }, - DestinationShards: []*topo.ShardInfo{ - topo.NewShardInfo("keyspace-1", "dst1", nil, nil), - topo.NewShardInfo("keyspace-1", "dst2", nil, nil), - }, - Reverse: true, - StatusUpdater: base.StatusUpdater{Status: "status"}, - } - gotSev, gotMsg := ev.Syslog() - - if gotSev != wantSev { - t.Errorf("wrong severity: got %v, want %v", gotSev, wantSev) - } - if gotMsg != wantMsg { - t.Errorf("wrong message: got %v, want %v", gotMsg, wantMsg) - } -} diff --git a/go/vt/vtctl/fakevtctlclient/fake_loggerevent_streamingclient.go b/go/vt/vtctl/fakevtctlclient/fake_loggerevent_streamingclient.go index 54f757ab60b..a11db6a4952 100644 --- a/go/vt/vtctl/fakevtctlclient/fake_loggerevent_streamingclient.go +++ b/go/vt/vtctl/fakevtctlclient/fake_loggerevent_streamingclient.go @@ -28,7 +28,7 @@ import ( logutilpb "vitess.io/vitess/go/vt/proto/logutil" ) -// FakeLoggerEventStreamingClient is the base for the fakes for the vtctlclient and vtworkerclient. +// FakeLoggerEventStreamingClient is the base for the fakes for vtctlclient. // It allows to register a (multi-)line string for a given command and return the result as channel which streams it back. type FakeLoggerEventStreamingClient struct { results map[string]*result diff --git a/go/vt/vtctl/grpcvtctldclient/client_gen.go b/go/vt/vtctl/grpcvtctldclient/client_gen.go index 7aebdeac843..30b57302dc7 100644 --- a/go/vt/vtctl/grpcvtctldclient/client_gen.go +++ b/go/vt/vtctl/grpcvtctldclient/client_gen.go @@ -542,15 +542,6 @@ func (client *gRPCVtctldClient) SetKeyspaceDurabilityPolicy(ctx context.Context, return client.c.SetKeyspaceDurabilityPolicy(ctx, in, opts...) } -// SetKeyspaceServedFrom is part of the vtctlservicepb.VtctldClient interface. -func (client *gRPCVtctldClient) SetKeyspaceServedFrom(ctx context.Context, in *vtctldatapb.SetKeyspaceServedFromRequest, opts ...grpc.CallOption) (*vtctldatapb.SetKeyspaceServedFromResponse, error) { - if client.c == nil { - return nil, status.Error(codes.Unavailable, connClosedMsg) - } - - return client.c.SetKeyspaceServedFrom(ctx, in, opts...) -} - // SetShardIsPrimaryServing is part of the vtctlservicepb.VtctldClient interface. func (client *gRPCVtctldClient) SetShardIsPrimaryServing(ctx context.Context, in *vtctldatapb.SetShardIsPrimaryServingRequest, opts ...grpc.CallOption) (*vtctldatapb.SetShardIsPrimaryServingResponse, error) { if client.c == nil { diff --git a/go/vt/vtctl/grpcvtctldserver/server.go b/go/vt/vtctl/grpcvtctldserver/server.go index 25a6edd4a0c..1a112737804 100644 --- a/go/vt/vtctl/grpcvtctldserver/server.go +++ b/go/vt/vtctl/grpcvtctldserver/server.go @@ -2498,21 +2498,6 @@ func (s *VtctldServer) SetKeyspaceServedFrom(ctx context.Context, req *vtctldata }, nil } -// SetKeyspaceShardingInfo is part of the vtctlservicepb.VtctldServer interface. -func (s *VtctldServer) SetKeyspaceShardingInfo(ctx context.Context, req *vtctldatapb.SetKeyspaceShardingInfoRequest) (*vtctldatapb.SetKeyspaceShardingInfoResponse, error) { - span, ctx := trace.NewSpan(ctx, "VtctldServer.SetKeyspaceShardingInfo") - defer span.Finish() - - ki, err := s.ts.GetKeyspace(ctx, req.Keyspace) - if err != nil { - return nil, err - } - - return &vtctldatapb.SetKeyspaceShardingInfoResponse{ - Keyspace: ki.Keyspace, - }, nil -} - // SetShardIsPrimaryServing is part of the vtctlservicepb.VtctldServer interface. func (s *VtctldServer) SetShardIsPrimaryServing(ctx context.Context, req *vtctldatapb.SetShardIsPrimaryServingRequest) (*vtctldatapb.SetShardIsPrimaryServingResponse, error) { span, ctx := trace.NewSpan(ctx, "VtctldServer.SetShardIsPrimaryServing") diff --git a/go/vt/vtctl/grpcvtctldserver/server_test.go b/go/vt/vtctl/grpcvtctldserver/server_test.go index c4d63a5fdac..ea28f31268b 100644 --- a/go/vt/vtctl/grpcvtctldserver/server_test.go +++ b/go/vt/vtctl/grpcvtctldserver/server_test.go @@ -8380,93 +8380,6 @@ func TestSetKeyspaceDurabilityPolicy(t *testing.T) { } } -func TestSetKeyspaceServedFrom(t *testing.T) { - t.Parallel() - - tests := []struct { - name string - keyspaces []*vtctldatapb.Keyspace - req *vtctldatapb.SetKeyspaceServedFromRequest - expected *vtctldatapb.SetKeyspaceServedFromResponse - shouldErr bool - }{ - { - name: "ok", - keyspaces: []*vtctldatapb.Keyspace{ - { - Name: "ks1", - Keyspace: &topodatapb.Keyspace{}, - }, - { - Name: "ks2", - Keyspace: &topodatapb.Keyspace{}, - }, - }, - req: &vtctldatapb.SetKeyspaceServedFromRequest{ - Keyspace: "ks1", - TabletType: topodatapb.TabletType_REPLICA, - Cells: []string{"zone1"}, - SourceKeyspace: "ks2", - }, - expected: &vtctldatapb.SetKeyspaceServedFromResponse{ - Keyspace: &topodatapb.Keyspace{ - ServedFroms: []*topodatapb.Keyspace_ServedFrom{ - { - TabletType: topodatapb.TabletType_REPLICA, - Cells: []string{"zone1"}, - Keyspace: "ks2", - }, - }, - }, - }, - }, - { - name: "keyspace not found", - req: &vtctldatapb.SetKeyspaceServedFromRequest{ - Keyspace: "ks1", - }, - shouldErr: true, - }, - { - name: "fail to update servedfrom map", - keyspaces: []*vtctldatapb.Keyspace{ - { - Name: "ks1", - Keyspace: &topodatapb.Keyspace{}, - }, - }, - req: &vtctldatapb.SetKeyspaceServedFromRequest{ - TabletType: topodatapb.TabletType_PRIMARY, - }, - shouldErr: true, - }, - } - - ctx := context.Background() - - for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { - t.Parallel() - - ts := memorytopo.NewServer("zone1") - testutil.AddKeyspaces(ctx, t, ts, tt.keyspaces...) - - vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) - }) - resp, err := vtctld.SetKeyspaceServedFrom(ctx, tt.req) - if tt.shouldErr { - assert.Error(t, err) - return - } - - require.NoError(t, err) - utils.MustMatch(t, tt.expected, resp) - }) - } -} - func TestSetShardIsPrimaryServing(t *testing.T) { t.Parallel() diff --git a/go/vt/vtctl/localvtctldclient/client_gen.go b/go/vt/vtctl/localvtctldclient/client_gen.go index 8ff8d672446..af049f4a6ba 100644 --- a/go/vt/vtctl/localvtctldclient/client_gen.go +++ b/go/vt/vtctl/localvtctldclient/client_gen.go @@ -452,11 +452,6 @@ func (client *localVtctldClient) SetKeyspaceDurabilityPolicy(ctx context.Context return client.s.SetKeyspaceDurabilityPolicy(ctx, in) } -// SetKeyspaceServedFrom is part of the vtctlservicepb.VtctldClient interface. -func (client *localVtctldClient) SetKeyspaceServedFrom(ctx context.Context, in *vtctldatapb.SetKeyspaceServedFromRequest, opts ...grpc.CallOption) (*vtctldatapb.SetKeyspaceServedFromResponse, error) { - return client.s.SetKeyspaceServedFrom(ctx, in) -} - // SetShardIsPrimaryServing is part of the vtctlservicepb.VtctldClient interface. func (client *localVtctldClient) SetShardIsPrimaryServing(ctx context.Context, in *vtctldatapb.SetShardIsPrimaryServingRequest, opts ...grpc.CallOption) (*vtctldatapb.SetShardIsPrimaryServingResponse, error) { return client.s.SetShardIsPrimaryServing(ctx, in) diff --git a/go/vt/vtctl/vtctl.go b/go/vt/vtctl/vtctl.go index 24006417298..3279a87f20b 100644 --- a/go/vt/vtctl/vtctl.go +++ b/go/vt/vtctl/vtctl.go @@ -63,9 +63,7 @@ COMMAND ARGUMENT DEFINITIONS for backup purposes -- batch: A replicated copy of data for OLAP load patterns (typically for MapReduce jobs) - -- drained: A tablet that is reserved for a background process. For example, - a tablet used by a vtworker process, where the tablet is likely - lagging in replication. + -- drained: A tablet that is reserved for a background process. -- experimental: A replica copy of data that is ready but not serving query traffic. The value indicates a special characteristic of the tablet that indicates the tablet should not be @@ -341,7 +339,7 @@ var commands = []commandGroup{ name: "UpdateSrvKeyspacePartition", method: commandUpdateSrvKeyspacePartition, params: "[--cells=c1,c2,...] [--remove] ", - help: "Updates KeyspaceGraph partition for a shard and tablet type. Only use this for an emergency fix during an horizontal shard split. Specify the remove flag, if you want the shard to be removed from the desired partition.", + help: "Updates KeyspaceGraph partition for a shard and tablet type. Only use this for emergency fixes. Specify the remove flag, if you want the shard to be removed from the desired partition.", }, { name: "SourceShardDelete", @@ -427,20 +425,6 @@ var commands = []commandGroup{ params: "", help: "Outputs a sorted list of all keyspaces.", }, - { - name: "SetKeyspaceShardingInfo", - method: commandSetKeyspaceShardingInfo, - params: "[--force] [] []", - help: "Updates the sharding information for a keyspace.", - deprecated: true, - }, - { - name: "SetKeyspaceServedFrom", - method: commandSetKeyspaceServedFrom, - params: "[--source=] [--remove] [--cells=c1,c2,...] ", - help: "Changes the ServedFromMap manually. This command is intended for emergency fixes. This command does not rebuild the serving graph.", - deprecated: true, - }, { name: "RebuildKeyspaceGraph", method: commandRebuildKeyspaceGraph, @@ -495,40 +479,12 @@ var commands = []commandGroup{ params: `[--cells=] [--tablet_types=] , example : '{"workflow": "aaa", "source_keyspace": "source", "target_keyspace": "target", "table_settings": [{"target_table": "customer", "source_expression": "select * from customer", "create_ddl": "copy"}]}'`, help: "Performs materialization based on the json spec. Is used directly to form VReplication rules, with an optional step to copy table structure/DDL.", }, - { - name: "SplitClone", - method: commandSplitClone, - params: " ", - help: "Start the SplitClone process to perform horizontal resharding. Example: SplitClone ks '0' '-80,80-'", - deprecated: true, - }, - { - name: "VerticalSplitClone", - method: commandVerticalSplitClone, - params: " ", - help: "Start the VerticalSplitClone process to perform vertical resharding. Example: SplitClone from_ks to_ks 'a,/b.*/'", - deprecated: true, - }, { name: "VDiff", method: commandVDiff, params: "[--source_cell=] [--target_cell=] [--tablet_types=in_order:RDONLY,REPLICA,PRIMARY] [--filtered_replication_wait_time=30s] [--max_extra_rows_to_compare=1000] ", help: "Perform a diff of all tables in the workflow", }, - { - name: "MigrateServedTypes", - method: commandMigrateServedTypes, - params: "[--cells=c1,c2,...] [--reverse] [--skip-refresh-state] [--filtered_replication_wait_time=30s] [--reverse_replication=false] ", - help: "Migrates a serving type from the source shard to the shards that it replicates to. This command also rebuilds the serving graph. The argument can specify any of the shards involved in the migration.", - deprecated: true, - }, - { - name: "MigrateServedFrom", - method: commandMigrateServedFrom, - params: "[--cells=c1,c2,...] [--reverse] [--filtered_replication_wait_time=30s] ", - help: "Makes the serve the given type. This command also rebuilds the serving graph.", - deprecated: true, - }, { name: "SwitchReads", method: commandSwitchReads, @@ -541,33 +497,12 @@ var commands = []commandGroup{ params: "[--timeout=30s] [--reverse] [--reverse_replication=true] [--dry_run] ", help: "Switch write traffic for the specified workflow.", }, - { - name: "CancelResharding", - method: commandCancelResharding, - params: "", - help: "Permanently cancels a resharding in progress. All resharding related metadata will be deleted.", - }, - { - name: "ShowResharding", - method: commandShowResharding, - params: "", - help: "Displays all metadata about a resharding in progress.", - }, { name: "FindAllShardsInKeyspace", method: commandFindAllShardsInKeyspace, params: "", help: "Displays all of the shards in the specified keyspace.", }, - { - name: "WaitForDrain", - method: commandWaitForDrain, - params: "[--timeout ] [--retry_delay ] [--initial_wait ] ", - help: "Blocks until no new queries were observed on all tablets with the given tablet type in the specified keyspace. " + - " This can be used as sanity check to ensure that the tablets were drained after running vtctl MigrateServedTypes " + - " and vtgate is no longer using them. If --timeout is set, it fails when the timeout is reached.", - deprecated: true, - }, { name: "Mount", method: commandMount, @@ -1325,39 +1260,6 @@ func commandIgnoreHealthError(ctx context.Context, wr *wrangler.Wrangler, subFla return fmt.Errorf("this command is no longer relevant and has been deprecated") } -func commandWaitForDrain(ctx context.Context, wr *wrangler.Wrangler, subFlags *flag.FlagSet, args []string) error { - wr.Logger().Printf("*** This is a legacy sharding command that will soon be removed! Please use VReplication instead: https://vitess.io/docs/reference/vreplication/ ***\n") - var cells flagutil.StringListValue - subFlags.Var(&cells, "cells", "Specifies a comma-separated list of cells to look for tablets") - timeout := subFlags.Duration("timeout", 0*time.Second, "Timeout after which the command fails") - retryDelay := subFlags.Duration("retry_delay", 1*time.Second, "Time to wait between two checks") - initialWait := subFlags.Duration("initial_wait", 1*time.Minute, "Time to wait for all tablets to check in") - - if err := subFlags.Parse(args); err != nil { - return err - } - if subFlags.NArg() != 2 { - return fmt.Errorf("the and arguments are both required for the WaitForDrain command") - } - if *timeout != 0 { - var cancel context.CancelFunc - ctx, cancel = context.WithTimeout(ctx, *timeout) - defer cancel() - } - - keyspace, shard, err := topoproto.ParseKeyspaceShard(subFlags.Arg(0)) - if err != nil { - return err - } - servedType, err := topo.ParseServingTabletType(subFlags.Arg(1)) - if err != nil { - return err - } - - return wr.WaitForDrain(ctx, cells, keyspace, shard, servedType, - *retryDelay, *HealthCheckTopologyRefresh, *HealthcheckRetryDelay, *HealthCheckTimeout, *initialWait) -} - func commandSleep(ctx context.Context, wr *wrangler.Wrangler, subFlags *flag.FlagSet, args []string) error { if err := subFlags.Parse(args); err != nil { return err @@ -1685,9 +1587,9 @@ func commandUpdateSrvKeyspacePartition(ctx context.Context, wr *wrangler.Wrangle func commandSetShardTabletControl(ctx context.Context, wr *wrangler.Wrangler, subFlags *flag.FlagSet, args []string) error { cellsStr := subFlags.String("cells", "", "Specifies a comma-separated list of cells to update") - deniedTablesStr := subFlags.String("denied_tables", "", "Specifies a comma-separated list of tables to add to the denylist (used for vertical split). Each is either an exact match, or a regular expression of the form '/regexp/'.") + deniedTablesStr := subFlags.String("denied_tables", "", "Specifies a comma-separated list of tables to add to the denylist (used for VReplication). Each is either an exact match, or a regular expression of the form '/regexp/'.") - remove := subFlags.Bool("remove", false, "Removes cells for vertical splits.") + remove := subFlags.Bool("remove", false, "Removes cells.") disableQueryService := subFlags.Bool("disable_query_service", false, "Disables query service on the provided nodes. This flag requires 'denied_tables' and 'remove' to be unset, otherwise it's ignored.") if err := subFlags.Parse(args); err != nil { return err @@ -1745,7 +1647,7 @@ func commandSourceShardDelete(ctx context.Context, wr *wrangler.Wrangler, subFla func commandSourceShardAdd(ctx context.Context, wr *wrangler.Wrangler, subFlags *flag.FlagSet, args []string) error { keyRange := subFlags.String("key_range", "", "Identifies the key range to use for the SourceShard") - tablesStr := subFlags.String("tables", "", "Specifies a comma-separated list of tables to replicate (used for vertical split). Each is either an exact match, or a regular expression of the form /regexp/") + tablesStr := subFlags.String("tables", "", "Specifies a comma-separated list of tables to replicate. Each is either an exact match, or a regular expression of the form /regexp/") if err := subFlags.Parse(args); err != nil { return err } @@ -2104,42 +2006,6 @@ func commandGetKeyspaces(ctx context.Context, wr *wrangler.Wrangler, subFlags *f return nil } -func commandSetKeyspaceShardingInfo(ctx context.Context, wr *wrangler.Wrangler, subFlags *flag.FlagSet, args []string) error { - wr.Logger().Printf("*** This is a legacy sharding command that will soon be removed! Please use VReplication instead: https://vitess.io/docs/reference/vreplication/ ***\n") - return nil -} - -func commandSetKeyspaceServedFrom(ctx context.Context, wr *wrangler.Wrangler, subFlags *flag.FlagSet, args []string) error { - wr.Logger().Printf("*** This is a legacy sharding command that will soon be removed! Please use VReplication instead: https://vitess.io/docs/reference/vreplication/ ***\n") - source := subFlags.String("source", "", "Specifies the source keyspace name") - remove := subFlags.Bool("remove", false, "Indicates whether to add (default) or remove the served from record") - cellsStr := subFlags.String("cells", "", "Specifies a comma-separated list of cells to affect") - if err := subFlags.Parse(args); err != nil { - return err - } - if subFlags.NArg() != 2 { - return fmt.Errorf("the and arguments are required for the SetKeyspaceServedFrom command") - } - keyspace := subFlags.Arg(0) - servedType, err := parseTabletType(subFlags.Arg(1), []topodatapb.TabletType{topodatapb.TabletType_PRIMARY, topodatapb.TabletType_REPLICA, topodatapb.TabletType_RDONLY}) - if err != nil { - return err - } - var cells []string - if *cellsStr != "" { - cells = strings.Split(*cellsStr, ",") - } - - _, err = wr.VtctldServer().SetKeyspaceServedFrom(ctx, &vtctldatapb.SetKeyspaceServedFromRequest{ - Keyspace: keyspace, - TabletType: servedType, - Cells: cells, - Remove: *remove, - SourceKeyspace: *source, - }) - return err -} - func commandRebuildKeyspaceGraph(ctx context.Context, wr *wrangler.Wrangler, subFlags *flag.FlagSet, args []string) error { cells := subFlags.String("cells", "", "Specifies a comma-separated list of cells to update") allowPartial := subFlags.Bool("allow_partial", false, "Specifies whether a SNAPSHOT keyspace is allowed to serve with an incomplete set of shards. Ignored for all other types of keyspaces") @@ -2698,34 +2564,6 @@ func commandMaterialize(ctx context.Context, wr *wrangler.Wrangler, subFlags *fl return wr.Materialize(ctx, ms) } -func commandSplitClone(ctx context.Context, wr *wrangler.Wrangler, subFlags *flag.FlagSet, args []string) error { - wr.Logger().Printf("*** This is a legacy sharding command that will soon be removed! Please use VReplication instead: https://vitess.io/docs/reference/vreplication/ ***\n") - if err := subFlags.Parse(args); err != nil { - return err - } - if subFlags.NArg() != 3 { - return fmt.Errorf("three arguments are required: keyspace, from_shards, to_shards") - } - keyspace := subFlags.Arg(0) - from := strings.Split(subFlags.Arg(1), ",") - to := strings.Split(subFlags.Arg(2), ",") - return wr.SplitClone(ctx, keyspace, from, to) -} - -func commandVerticalSplitClone(ctx context.Context, wr *wrangler.Wrangler, subFlags *flag.FlagSet, args []string) error { - wr.Logger().Printf("*** This is a legacy sharding command that will soon be removed! Please use VReplication instead: https://vitess.io/docs/reference/vreplication/ ***\n") - if err := subFlags.Parse(args); err != nil { - return err - } - if subFlags.NArg() != 3 { - return fmt.Errorf("three arguments are required: from_keyspace, to_keyspace, tables") - } - fromKeyspace := subFlags.Arg(0) - toKeyspace := subFlags.Arg(1) - tables := strings.Split(subFlags.Arg(2), ",") - return wr.VerticalSplitClone(ctx, fromKeyspace, toKeyspace, tables) -} - func useVDiffV2(args []string) bool { for _, arg := range args { if arg == "-v2" || arg == "--v2" { @@ -2793,65 +2631,6 @@ func splitKeyspaceWorkflow(in string) (keyspace, workflow string, err error) { return splits[0], splits[1], nil } -func commandMigrateServedTypes(ctx context.Context, wr *wrangler.Wrangler, subFlags *flag.FlagSet, args []string) error { - wr.Logger().Printf("*** This is a legacy sharding command that will soon be removed! Please use VReplication instead: https://vitess.io/docs/reference/vreplication/ ***\n") - cellsStr := subFlags.String("cells", "", "Specifies a comma-separated list of cells to update") - reverse := subFlags.Bool("reverse", false, "Moves the served tablet type backward instead of forward.") - skipReFreshState := subFlags.Bool("skip-refresh-state", false, "Skips refreshing the state of the source tablets after the migration, meaning that the refresh will need to be done manually, REPLICA and RDONLY only)") - filteredReplicationWaitTime := subFlags.Duration("filtered_replication_wait_time", 30*time.Second, "Specifies the maximum time to wait, in seconds, for filtered replication to catch up on primary migrations. The migration will be cancelled on a timeout.") - reverseReplication := subFlags.Bool("reverse_replication", false, "For primary migration, enabling this flag reverses replication which allows you to rollback") - if err := subFlags.Parse(args); err != nil { - return err - } - if subFlags.NArg() != 2 { - return fmt.Errorf("the and arguments are both required for the MigrateServedTypes command") - } - - keyspace, shard, err := topoproto.ParseKeyspaceShard(subFlags.Arg(0)) - if err != nil { - return err - } - servedType, err := topo.ParseServingTabletType(subFlags.Arg(1)) - if err != nil { - return err - } - if servedType == topodatapb.TabletType_PRIMARY && *skipReFreshState { - return fmt.Errorf("the skip-refresh-state flag can only be specified for non-primary migrations") - } - var cells []string - if *cellsStr != "" { - cells = strings.Split(*cellsStr, ",") - } - return wr.MigrateServedTypes(ctx, keyspace, shard, cells, servedType, *reverse, *skipReFreshState, *filteredReplicationWaitTime, *reverseReplication) -} - -func commandMigrateServedFrom(ctx context.Context, wr *wrangler.Wrangler, subFlags *flag.FlagSet, args []string) error { - wr.Logger().Printf("*** This is a legacy sharding command that will soon be removed! Please use VReplication instead: https://vitess.io/docs/reference/vreplication/ ***\n") - reverse := subFlags.Bool("reverse", false, "Moves the served tablet type backward instead of forward.") - cellsStr := subFlags.String("cells", "", "Specifies a comma-separated list of cells to update") - filteredReplicationWaitTime := subFlags.Duration("filtered_replication_wait_time", 30*time.Second, "Specifies the maximum time to wait, in seconds, for filtered replication to catch up on primary migrations. The migration will be cancelled on a timeout.") - if err := subFlags.Parse(args); err != nil { - return err - } - if subFlags.NArg() != 2 { - return fmt.Errorf("the and arguments are both required for the MigrateServedFrom command") - } - - keyspace, shard, err := topoproto.ParseKeyspaceShard(subFlags.Arg(0)) - if err != nil { - return err - } - servedType, err := parseTabletType(subFlags.Arg(1), []topodatapb.TabletType{topodatapb.TabletType_PRIMARY, topodatapb.TabletType_REPLICA, topodatapb.TabletType_RDONLY}) - if err != nil { - return err - } - var cells []string - if *cellsStr != "" { - cells = strings.Split(*cellsStr, ",") - } - return wr.MigrateServedFrom(ctx, keyspace, shard, servedType, cells, *reverse, *filteredReplicationWaitTime) -} - func commandDropSources(ctx context.Context, wr *wrangler.Wrangler, subFlags *flag.FlagSet, args []string) error { wr.Logger().Printf("*** DropSources is deprecated. Consider using v2 commands instead, see https://vitess.io/docs/reference/vreplication/v2/ ***\n") dryRun := subFlags.Bool("dry_run", false, "Does a dry run of commandDropSources and only reports the actions to be taken") @@ -2978,36 +2757,6 @@ func commandSwitchWrites(ctx context.Context, wr *wrangler.Wrangler, subFlags *f return nil } -func commandCancelResharding(ctx context.Context, wr *wrangler.Wrangler, subFlags *flag.FlagSet, args []string) error { - if err := subFlags.Parse(args); err != nil { - return err - } - if subFlags.NArg() != 1 { - return fmt.Errorf(" required for CancelResharding command") - } - - keyspace, shard, err := topoproto.ParseKeyspaceShard(subFlags.Arg(0)) - if err != nil { - return err - } - return wr.CancelResharding(ctx, keyspace, shard) -} - -func commandShowResharding(ctx context.Context, wr *wrangler.Wrangler, subFlags *flag.FlagSet, args []string) error { - if err := subFlags.Parse(args); err != nil { - return err - } - if subFlags.NArg() != 1 { - return fmt.Errorf(" required for ShowResharding command") - } - - keyspace, shard, err := topoproto.ParseKeyspaceShard(subFlags.Arg(0)) - if err != nil { - return err - } - return wr.ShowResharding(ctx, keyspace, shard) -} - func commandFindAllShardsInKeyspace(ctx context.Context, wr *wrangler.Wrangler, subFlags *flag.FlagSet, args []string) error { if err := subFlags.Parse(args); err != nil { return err diff --git a/go/vt/vtctld/workflow.go b/go/vt/vtctld/workflow.go index d9f4c46a9ee..d4bda3cface 100644 --- a/go/vt/vtctld/workflow.go +++ b/go/vt/vtctld/workflow.go @@ -29,8 +29,6 @@ import ( "vitess.io/vitess/go/vt/topo" "vitess.io/vitess/go/vt/vtctl" "vitess.io/vitess/go/vt/workflow" - "vitess.io/vitess/go/vt/workflow/resharding" - "vitess.io/vitess/go/vt/workflow/reshardingworkflowgen" "vitess.io/vitess/go/vt/workflow/topovalidator" ) @@ -54,12 +52,6 @@ func initWorkflowManager(ts *topo.Server) { topovalidator.RegisterShardValidator() topovalidator.Register() - // Register the Horizontal Resharding workflow. - resharding.Register() - - // Register workflow that generates Horizontal Resharding workflows. - reshardingworkflowgen.Register() - // Unregister the disabled workflows. for _, name := range workflowManagerDisable { workflow.Unregister(name) diff --git a/go/vt/vttablet/tabletmanager/rpc_actions.go b/go/vt/vttablet/tabletmanager/rpc_actions.go index c4ccab6cbc4..1093c331a1a 100644 --- a/go/vt/vttablet/tabletmanager/rpc_actions.go +++ b/go/vt/vttablet/tabletmanager/rpc_actions.go @@ -87,9 +87,7 @@ func (tm *TabletManager) ChangeType(ctx context.Context, tabletType topodatapb.T // ChangeType changes the tablet type func (tm *TabletManager) changeTypeLocked(ctx context.Context, tabletType topodatapb.TabletType, action DBAction, semiSync SemiSyncAction) error { - // We don't want to allow multiple callers to claim a tablet as drained. There is a race that could happen during - // horizontal resharding where two vtworkers will try to DRAIN the same tablet. This check prevents that race from - // causing errors. + // We don't want to allow multiple callers to claim a tablet as drained. if tabletType == topodatapb.TabletType_DRAINED && tm.Tablet().Type == topodatapb.TabletType_DRAINED { return fmt.Errorf("Tablet: %v, is already drained", tm.tabletAlias) } diff --git a/go/vt/vttablet/tabletserver/query_executor.go b/go/vt/vttablet/tabletserver/query_executor.go index 76131aea182..d6b27aa7dfa 100644 --- a/go/vt/vttablet/tabletserver/query_executor.go +++ b/go/vt/vttablet/tabletserver/query_executor.go @@ -414,7 +414,6 @@ func (qre *QueryExecutor) checkPermissions() error { } // Skip the ACL check if the connecting user is an exempted superuser. - // Necessary to whitelist e.g. direct vtworker access. if qre.tsv.qe.exemptACL != nil && qre.tsv.qe.exemptACL.IsMember(&querypb.VTGateCallerID{Username: username}) { qre.tsv.qe.tableaclExemptCount.Add(1) return nil diff --git a/go/vt/worker/block.go b/go/vt/worker/block.go deleted file mode 100644 index 23960ded61c..00000000000 --- a/go/vt/worker/block.go +++ /dev/null @@ -1,100 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package worker - -import ( - "html/template" - - "context" - - "vitess.io/vitess/go/vt/proto/vtrpc" - "vitess.io/vitess/go/vt/vterrors" - - "vitess.io/vitess/go/vt/wrangler" -) - -// BlockWorker will block infinitely until its context is canceled. -type BlockWorker struct { - StatusWorker - - // We use the Wrangler's logger to print the message. - wr *wrangler.Wrangler -} - -// NewBlockWorker returns a new BlockWorker object. -func NewBlockWorker(wr *wrangler.Wrangler) (Worker, error) { - return &BlockWorker{ - StatusWorker: NewStatusWorker(), - wr: wr, - }, nil -} - -// StatusAsHTML implements the Worker interface. -func (bw *BlockWorker) StatusAsHTML() template.HTML { - state := bw.State() - - result := "Block Command (blocking infinitely until context is canceled)
\n" - result += "State: " + state.String() + "
\n" - switch state { - case WorkerStateDebugRunning: - result += "Running (blocking)
\n" - case WorkerStateDone: - result += "Success (unblocked)
\n" - } - - return template.HTML(result) -} - -// StatusAsText implements the Worker interface. -func (bw *BlockWorker) StatusAsText() string { - state := bw.State() - - result := "Block Command\n" - result += "State: " + state.String() + "\n" - switch state { - case WorkerStateDebugRunning: - result += "Running (blocking)\n" - case WorkerStateDone: - result += "Success (unblocked)\n" - } - return result -} - -// Run implements the Worker interface. -func (bw *BlockWorker) Run(ctx context.Context) error { - resetVars() - err := bw.run(ctx) - - bw.SetState(WorkerStateCleanUp) - if err != nil { - bw.SetState(WorkerStateError) - return err - } - bw.SetState(WorkerStateDone) - return nil -} - -func (bw *BlockWorker) run(ctx context.Context) error { - // We reuse the Copy state to reflect that the blocking is in progress. - bw.SetState(WorkerStateDebugRunning) - bw.wr.Logger().Printf("Block command was called and will block infinitely until the RPC context is canceled.\n") - <-ctx.Done() - bw.wr.Logger().Printf("Block command finished because the context is done: '%v'.\n", ctx.Err()) - bw.SetState(WorkerStateDone) - - return vterrors.New(vtrpc.Code_CANCELED, "command 'Block' was canceled") -} diff --git a/go/vt/worker/block_cmd.go b/go/vt/worker/block_cmd.go deleted file mode 100644 index e9c51e168ff..00000000000 --- a/go/vt/worker/block_cmd.go +++ /dev/null @@ -1,90 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package worker - -import ( - "flag" - "html/template" - "net/http" - - "vitess.io/vitess/go/vt/proto/vtrpc" - "vitess.io/vitess/go/vt/vterrors" - - "context" - - "vitess.io/vitess/go/vt/wrangler" -) - -const blockHTML = ` - - - Block Action - - -

Block Action

- - {{if .Error}} - Error: {{.Error}}
- {{else}} -
- -
- {{end}} - -` - -var blockTemplate = mustParseTemplate("block", blockHTML) - -func commandBlock(wi *Instance, wr *wrangler.Wrangler, subFlags *flag.FlagSet, args []string) (Worker, error) { - if err := subFlags.Parse(args); err != nil { - return nil, err - } - if subFlags.NArg() != 0 { - subFlags.Usage() - return nil, vterrors.New(vtrpc.Code_INVALID_ARGUMENT, "command Block does not accept any parameter") - } - - worker, err := NewBlockWorker(wr) - if err != nil { - return nil, vterrors.Wrap(err, "Could not create Block worker") - } - return worker, nil -} - -func interactiveBlock(ctx context.Context, wi *Instance, wr *wrangler.Wrangler, w http.ResponseWriter, r *http.Request) (Worker, *template.Template, map[string]any, error) { - if err := r.ParseForm(); err != nil { - return nil, nil, nil, vterrors.Wrap(err, "cannot parse form") - } - - if submit := r.FormValue("submit"); submit == "" { - result := make(map[string]any) - return nil, blockTemplate, result, nil - } - - wrk, err := NewBlockWorker(wr) - if err != nil { - return nil, nil, nil, vterrors.Wrap(err, "Could not create Block worker") - } - return wrk, nil, nil, nil -} - -func init() { - AddCommand("Debugging", Command{"Block", - commandBlock, interactiveBlock, - "", - "For internal tests only. When triggered, the command will block until canceled."}) -} diff --git a/go/vt/worker/chunk.go b/go/vt/worker/chunk.go deleted file mode 100644 index faed667f3a6..00000000000 --- a/go/vt/worker/chunk.go +++ /dev/null @@ -1,200 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package worker - -import ( - "fmt" - - "vitess.io/vitess/go/vt/vtgate/evalengine" - - "vitess.io/vitess/go/vt/proto/vtrpc" - "vitess.io/vitess/go/vt/vterrors" - - "context" - - "vitess.io/vitess/go/sqlescape" - "vitess.io/vitess/go/sqltypes" - "vitess.io/vitess/go/vt/topo/topoproto" - "vitess.io/vitess/go/vt/wrangler" - - tabletmanagerdatapb "vitess.io/vitess/go/vt/proto/tabletmanagerdata" - topodatapb "vitess.io/vitess/go/vt/proto/topodata" -) - -var ( - completeChunk = chunk{sqltypes.NULL, sqltypes.NULL, 1, 1} - singleCompleteChunk = []chunk{completeChunk} -) - -// chunk holds the information which subset of the table should be worked on. -// The subset is the range of rows in the range [start, end) where start and end -// both refer to the first column of the primary key. -// If the column is not numeric, both start and end will be sqltypes.NULL. -type chunk struct { - start sqltypes.Value - end sqltypes.Value - // number records the position of this chunk among all "total" chunks. - // The lowest value is 1. - number int - // total is the total number of chunks this chunk belongs to. - total int -} - -// String returns a human-readable presentation of the chunk range. -func (c chunk) String() string { - // Pad the chunk number such that all log messages align nicely. - digits := digits(c.total) - return fmt.Sprintf("%*d/%d", digits, c.number, c.total) -} - -func digits(i int) int { - digits := 1 - for { - i /= 10 - if i == 0 { - break - } - digits++ - } - return digits -} - -// generateChunks returns an array of chunks to use for splitting up a table -// into multiple data chunks. It only works for tables with a primary key -// whose first column is a numeric type. -func generateChunks(ctx context.Context, wr *wrangler.Wrangler, tablet *topodatapb.Tablet, td *tabletmanagerdatapb.TableDefinition, chunkCount, minRowsPerChunk int) ([]chunk, error) { - if len(td.PrimaryKeyColumns) == 0 { - // No explicit primary key. Cannot chunk the rows then. - wr.Logger().Infof("table=%v: Not splitting the table into multiple chunks because it has no primary key columns. This will reduce the performance of the clone.", td.Name) - return singleCompleteChunk, nil - } - if td.RowCount < 2*uint64(minRowsPerChunk) { - // The automatic adjustment of "chunkCount" based on "minRowsPerChunk" - // below would set "chunkCount" to less than 2 i.e. 1 or 0 chunks. - // In practice in this case there should be exactly one chunk. - // Return early in this case and notice the user about this. - wr.Logger().Infof("table=%v: Not splitting the table into multiple chunks because it has only %d rows.", td.Name, td.RowCount) - return singleCompleteChunk, nil - } - if chunkCount == 1 { - return singleCompleteChunk, nil - } - - // Get the MIN and MAX of the leading column of the primary key. - query := fmt.Sprintf("SELECT MIN(%v), MAX(%v) FROM %v.%v", sqlescape.EscapeID(td.PrimaryKeyColumns[0]), sqlescape.EscapeID(td.PrimaryKeyColumns[0]), sqlescape.EscapeID(topoproto.TabletDbName(tablet)), sqlescape.EscapeID(td.Name)) - shortCtx, cancel := context.WithTimeout(ctx, *remoteActionsTimeout) - qr, err := wr.TabletManagerClient().ExecuteFetchAsApp(shortCtx, tablet, true, []byte(query), 1) - cancel() - if err != nil { - return nil, vterrors.Wrapf(err, "tablet: %v, table: %v: cannot determine MIN and MAX of the first primary key column. ExecuteFetchAsApp", topoproto.TabletAliasString(tablet.Alias), td.Name) - } - if len(qr.Rows) != 1 { - return nil, vterrors.Errorf(vtrpc.Code_FAILED_PRECONDITION, "tablet: %v, table: %v: cannot determine MIN and MAX of the first primary key column. Zero rows were returned", topoproto.TabletAliasString(tablet.Alias), td.Name) - } - - result := sqltypes.Proto3ToResult(qr) - min, _ := evalengine.ToNative(result.Rows[0][0]) - max, _ := evalengine.ToNative(result.Rows[0][1]) - - if min == nil || max == nil { - wr.Logger().Infof("table=%v: Not splitting the table into multiple chunks, min or max is NULL: %v", td.Name, qr.Rows[0]) - return singleCompleteChunk, nil - } - - // Determine the average number of rows per chunk for the given chunkCount. - avgRowsPerChunk := td.RowCount / uint64(chunkCount) - if avgRowsPerChunk < uint64(minRowsPerChunk) { - // Reduce the chunkCount to fulfill minRowsPerChunk. - newChunkCount := td.RowCount / uint64(minRowsPerChunk) - wr.Logger().Infof("table=%v: Reducing the number of chunks from the default %d to %d to make sure that each chunk has at least %d rows.", td.Name, chunkCount, newChunkCount, minRowsPerChunk) - chunkCount = int(newChunkCount) - } - - // TODO(mberlin): Write a unit test for this part of the function. - var interval any - chunks := make([]chunk, chunkCount) - switch min := min.(type) { - case int64: - max := max.(int64) - interval = (max - min) / int64(chunkCount) - if interval == 0 { - wr.Logger().Infof("table=%v: Not splitting the table into multiple chunks, interval=0: %v to %v", td.Name, min, max) - return singleCompleteChunk, nil - } - case uint64: - max := max.(uint64) - interval = (max - min) / uint64(chunkCount) - if interval == 0 { - wr.Logger().Infof("table=%v: Not splitting the table into multiple chunks, interval=0: %v to %v", td.Name, min, max) - return singleCompleteChunk, nil - } - case float64: - max := max.(float64) - interval = (max - min) / float64(chunkCount) - if interval == 0 { - wr.Logger().Infof("table=%v: Not splitting the table into multiple chunks, interval=0: %v to %v", td.Name, min, max) - return singleCompleteChunk, nil - } - default: - wr.Logger().Infof("table=%v: Not splitting the table into multiple chunks, primary key not numeric.", td.Name) - return singleCompleteChunk, nil - } - - // Create chunks. - start := min - for i := 0; i < chunkCount; i++ { - end := add(start, interval) - chunk, err := toChunk(start, end, i+1, chunkCount) - if err != nil { - return nil, vterrors.Wrapf(err, "tablet: %v, table: %v", topoproto.TabletAliasString(tablet.Alias), td.Name) - } - chunks[i] = chunk - start = end - } - - // Clear out the MIN and MAX on the first and last chunk respectively - // because other shards might have smaller or higher values than the one we - // looked at. - chunks[0].start = sqltypes.NULL - chunks[chunkCount-1].end = sqltypes.NULL - return chunks, nil -} - -func add(start, interval any) any { - switch start := start.(type) { - case int64: - return start + interval.(int64) - case uint64: - return start + interval.(uint64) - case float64: - return start + interval.(float64) - default: - panic(fmt.Sprintf("unsupported type %T for interval start: %v", start, start)) - } -} - -func toChunk(start, end any, number, total int) (chunk, error) { - startValue, err := sqltypes.InterfaceToValue(start) - if err != nil { - return chunk{}, vterrors.Wrapf(err, "failed to convert calculated start value (%v) into internal sqltypes.Value", start) - } - endValue, err := sqltypes.InterfaceToValue(end) - if err != nil { - return chunk{}, vterrors.Wrapf(err, "failed to convert calculated end value (%v) into internal sqltypes.Value", end) - } - return chunk{startValue, endValue, number, total}, nil -} diff --git a/go/vt/worker/clone_utils.go b/go/vt/worker/clone_utils.go deleted file mode 100644 index 1d854cc2613..00000000000 --- a/go/vt/worker/clone_utils.go +++ /dev/null @@ -1,34 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package worker - -import ( - "regexp" - - "vitess.io/vitess/go/sqlescape" -) - -// -// This file contains utility functions for clone workers. -// - -var errExtract = regexp.MustCompile(`\(errno (\d+)\)`) - -// escapeAll runs sqlescape.EscapeID() for all entries in the slice. -func escapeAll(identifiers []string) []string { - return sqlescape.EscapeIDs(identifiers) -} diff --git a/go/vt/worker/command.go b/go/vt/worker/command.go deleted file mode 100644 index a96ad90d087..00000000000 --- a/go/vt/worker/command.go +++ /dev/null @@ -1,180 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package worker - -import ( - "flag" - "html/template" - "net/http" - "os" - "strings" - "time" - - "context" - - "vitess.io/vitess/go/vt/proto/vtrpc" - - "vitess.io/vitess/go/vt/log" - "vitess.io/vitess/go/vt/logutil" - "vitess.io/vitess/go/vt/vterrors" - "vitess.io/vitess/go/vt/wrangler" -) - -// Command contains the detail of a command which can be run in vtworker. -// While "Method" is run from the command line or RPC, "Interactive" may contain -// special logic to parse a web form and return templated HTML output. -type Command struct { - Name string - Method func(wi *Instance, wr *wrangler.Wrangler, subFlags *flag.FlagSet, args []string) (Worker, error) - Interactive func(ctx context.Context, wi *Instance, wr *wrangler.Wrangler, w http.ResponseWriter, r *http.Request) (Worker, *template.Template, map[string]any, error) - Params string - Help string // if help is empty, won't list the command -} - -type commandGroup struct { - Name string - Description string - Commands []Command -} - -// commands is the list of available command groups. -var commands = []commandGroup{ - { - "Diffs", - "Workers comparing and validating data", - []Command{}, - }, - { - "Clones", - "Workers copying data for backups and clones", - []Command{}, - }, - { - "Debugging", - "Internal commands to test the general worker functionality", - []Command{}, - }, -} - -// AddCommand registers a command and makes it available. -func AddCommand(groupName string, c Command) { - for i, group := range commands { - if group.Name == groupName { - commands[i].Commands = append(commands[i].Commands, c) - return - } - } - panic(vterrors.Errorf(vtrpc.Code_INVALID_ARGUMENT, "trying to add to missing group %v", groupName)) -} - -func commandWorker(wi *Instance, wr *wrangler.Wrangler, args []string, cell string, runFromCli bool) (Worker, error) { - action := args[0] - - actionLowerCase := strings.ToLower(action) - for _, group := range commands { - for _, cmd := range group.Commands { - if strings.ToLower(cmd.Name) == actionLowerCase { - var subFlags *flag.FlagSet - if runFromCli { - subFlags = flag.NewFlagSet(action, flag.ExitOnError) - } else { - subFlags = flag.NewFlagSet(action, flag.ContinueOnError) - } - // The command may be run from an RPC and may not log to the console. - // The Wrangler logger defines where the output has to go. - subFlags.SetOutput(logutil.NewLoggerWriter(wr.Logger())) - subFlags.Usage = func() { - wr.Logger().Printf("Usage: %s %s %s\n\n", os.Args[0], cmd.Name, cmd.Params) - wr.Logger().Printf("%s\n\n", cmd.Help) - subFlags.PrintDefaults() - } - return cmd.Method(wi, wr, subFlags, args[1:]) - } - } - } - if runFromCli { - flag.Usage() - } else { - PrintAllCommands(wr.Logger()) - } - return nil, vterrors.Errorf(vtrpc.Code_INVALID_ARGUMENT, "unknown command: %v", action) -} - -// RunCommand executes the vtworker command specified by "args". Use WaitForCommand() to block on the returned done channel. -// If wr is nil, the default wrangler will be used. -// If you pass a wr wrangler, note that a MemoryLogger will be added to its current logger. -// The returned worker and done channel may be nil if no worker was started e.g. in case of a "Reset". -func (wi *Instance) RunCommand(ctx context.Context, args []string, wr *wrangler.Wrangler, runFromCli bool) (Worker, chan struct{}, error) { - if len(args) >= 1 { - switch args[0] { - case "Reset": - return nil, nil, wi.Reset() - case "Cancel": - wi.Cancel() - return nil, nil, nil - } - } - - if wr == nil { - wr = wi.wr - } - wrk, err := commandWorker(wi, wr, args, wi.cell, runFromCli) - if err != nil { - return nil, nil, err - } - done, err := wi.setAndStartWorker(ctx, wrk, wr) - if err != nil { - return nil, nil, vterrors.Wrap(err, "cannot set worker") - } - return wrk, done, nil -} - -// WaitForCommand blocks until "done" is closed. In the meantime, it logs the status of "wrk". -func (wi *Instance) WaitForCommand(wrk Worker, done chan struct{}) error { - // display the status every second - timer := time.NewTicker(wi.commandDisplayInterval) - defer timer.Stop() - for { - select { - case <-done: - log.Info(wrk.StatusAsText()) - wi.currentWorkerMutex.Lock() - err := wi.lastRunError - wi.currentWorkerMutex.Unlock() - if err != nil { - return err - } - return nil - case <-timer.C: - log.Info(wrk.StatusAsText()) - } - } -} - -// PrintAllCommands prints a help text for all registered commands to the given Logger. -func PrintAllCommands(logger logutil.Logger) { - for _, group := range commands { - if group.Name == "Debugging" { - continue - } - logger.Printf("%v: %v\n", group.Name, group.Description) - for _, cmd := range group.Commands { - logger.Printf(" %v %v\n", cmd.Name, cmd.Params) - } - logger.Printf("\n") - } -} diff --git a/go/vt/worker/defaults.go b/go/vt/worker/defaults.go deleted file mode 100644 index 1ef1ae58a48..00000000000 --- a/go/vt/worker/defaults.go +++ /dev/null @@ -1,57 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package worker - -import "vitess.io/vitess/go/vt/throttler" - -const ( - defaultOnline = true - defaultOffline = true - // defaultChunkCount is the number of chunks in which each table should be - // divided. One chunk is processed by one chunk pipeline at a time. - // -source_reader_count defines the number of concurrent chunk pipelines. - defaultChunkCount = 1000 - // defaultMinRowsPerChunk is the minimum number of rows a chunk should have - // on average. If this is not guaranteed, --chunk_count will be reduced - // automatically. - defaultMinRowsPerChunk = 10 * 1000 - defaultSourceReaderCount = 10 - // defaultWriteQueryMaxRows aggregates up to 100 rows per INSERT or DELETE - // query. Higher values are not recommended to avoid overloading MySQL. - // The actual row count will be less if defaultWriteQueryMaxSize is reached - // first, but always at least 1 row. - defaultWriteQueryMaxRows = 100 - // defaultWriteQueryMaxSize caps the write queries which aggregate multiple - // rows. This limit prevents e.g. that MySQL will OOM. - defaultWriteQueryMaxSize = 1024 * 1024 - // defaultDestinationPackCount is deprecated in favor of the writeQueryMax* - // values and currently only used by VerticalSplitClone. - // defaultDestinationPackCount is the number of StreamExecute responses which - // will be aggreated into one transaction. See the vttablet flag - // "-queryserver-config-stream-buffer-size" for the size (in bytes) of a - // StreamExecute response. As of 06/2015, the default for it was 32 kB. - // Note that higher values for this flag --destination_pack_count will - // increase memory consumption in vtworker, vttablet and mysql. - // defaultDestinationPackCount = 10 - defaultDestinationWriterCount = 20 - defaultMinHealthyTablets = 2 - defaultDestTabletType = "RDONLY" - defaultParallelDiffsCount = 8 - defaultMaxTPS = throttler.MaxRateModuleDisabled - defaultMaxReplicationLag = throttler.ReplicationLagModuleDisabled - defaultUseConsistentSnapshot = false -) diff --git a/go/vt/worker/diff_utils.go b/go/vt/worker/diff_utils.go deleted file mode 100644 index 259987d76e7..00000000000 --- a/go/vt/worker/diff_utils.go +++ /dev/null @@ -1,721 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package worker - -import ( - "bytes" - "encoding/binary" - "fmt" - "io" - "strings" - "time" - - "google.golang.org/protobuf/proto" - - "vitess.io/vitess/go/vt/vtgate/evalengine" - - "vitess.io/vitess/go/vt/proto/vtrpc" - "vitess.io/vitess/go/vt/vterrors" - "vitess.io/vitess/go/vt/vttablet/tmclient" - "vitess.io/vitess/go/vt/wrangler" - - "context" - - "vitess.io/vitess/go/sqlescape" - "vitess.io/vitess/go/sqltypes" - "vitess.io/vitess/go/vt/grpcclient" - "vitess.io/vitess/go/vt/key" - "vitess.io/vitess/go/vt/logutil" - "vitess.io/vitess/go/vt/proto/query" - "vitess.io/vitess/go/vt/topo" - "vitess.io/vitess/go/vt/topo/topoproto" - "vitess.io/vitess/go/vt/vtgate/vindexes" - "vitess.io/vitess/go/vt/vttablet/queryservice" - "vitess.io/vitess/go/vt/vttablet/tabletconn" - - querypb "vitess.io/vitess/go/vt/proto/query" - tabletmanagerdatapb "vitess.io/vitess/go/vt/proto/tabletmanagerdata" - topodatapb "vitess.io/vitess/go/vt/proto/topodata" -) - -// QueryResultReader will stream rows towards the output channel. -// TODO(mberlin): Delete this in favor of RestartableResultReader once -// we are confident that the new SplitClone code produces the same diff results -// as the old diff code. -type QueryResultReader struct { - output sqltypes.ResultStream - fields []*querypb.Field - conn queryservice.QueryService -} - -// NewQueryResultReaderForTablet creates a new QueryResultReader for -// the provided tablet / sql query -func NewQueryResultReaderForTablet(ctx context.Context, ts *topo.Server, tabletAlias *topodatapb.TabletAlias, sql string) (*QueryResultReader, error) { - shortCtx, cancel := context.WithTimeout(ctx, *remoteActionsTimeout) - tablet, err := ts.GetTablet(shortCtx, tabletAlias) - cancel() - if err != nil { - return nil, err - } - - conn, err := tabletconn.GetDialer()(tablet.Tablet, grpcclient.FailFast(false)) - if err != nil { - return nil, err - } - - stream := queryservice.ExecuteWithStreamer(ctx, conn, &querypb.Target{ - Keyspace: tablet.Tablet.Keyspace, - Shard: tablet.Tablet.Shard, - TabletType: tablet.Tablet.Type, - }, sql, make(map[string]*querypb.BindVariable), nil) - - // read the columns, or grab the error - cols, err := stream.Recv() - if err != nil { - return nil, vterrors.Wrapf(err, "Cannot read Fields for query '%v'", sql) - } - - return &QueryResultReader{ - output: stream, - fields: cols.Fields, - conn: conn, - }, nil -} - -// NewTransactionalQueryResultReaderForTablet creates a new QueryResultReader for -// the provided tablet / sql query, and runs it in an existing transaction -func NewTransactionalQueryResultReaderForTablet(ctx context.Context, ts *topo.Server, tabletAlias *topodatapb.TabletAlias, sql string, txID int64) (*QueryResultReader, error) { - shortCtx, cancel := context.WithTimeout(ctx, *remoteActionsTimeout) - tablet, err := ts.GetTablet(shortCtx, tabletAlias) - cancel() - if err != nil { - return nil, err - } - - conn, err := tabletconn.GetDialer()(tablet.Tablet, grpcclient.FailFast(false)) - if err != nil { - return nil, err - } - - stream := queryservice.ExecuteWithTransactionalStreamer(ctx, conn, &querypb.Target{ - Keyspace: tablet.Tablet.Keyspace, - Shard: tablet.Tablet.Shard, - TabletType: tablet.Tablet.Type, - }, sql, make(map[string]*querypb.BindVariable), txID, nil) - - // read the columns, or grab the error - cols, err := stream.Recv() - if err != nil { - return nil, vterrors.Wrapf(err, "cannot read Fields for query '%v'", sql) - } - - return &QueryResultReader{ - output: stream, - fields: cols.Fields, - conn: conn, - }, nil -} - -// Next returns the next result on the stream. It implements ResultReader. -func (qrr *QueryResultReader) Next() (*sqltypes.Result, error) { - return qrr.output.Recv() -} - -// Fields returns the field data. It implements ResultReader. -func (qrr *QueryResultReader) Fields() []*querypb.Field { - return qrr.fields -} - -// Close closes the connection to the tablet. -func (qrr *QueryResultReader) Close(ctx context.Context) { - qrr.conn.Close(ctx) -} - -// v3KeyRangeFilter is a sqltypes.ResultStream implementation that filters -// the underlying results to match the keyrange using a v3 resolver. -type v3KeyRangeFilter struct { - input sqltypes.ResultStream - resolver *v3Resolver - keyRange *topodatapb.KeyRange -} - -// Recv is part of sqltypes.ResultStream interface. -func (f *v3KeyRangeFilter) Recv() (*sqltypes.Result, error) { - r, err := f.input.Recv() - if err != nil { - return nil, err - } - - rows := make([][]sqltypes.Value, 0, len(r.Rows)) - for _, row := range r.Rows { - ksid, err := f.resolver.keyspaceID(row) - if err != nil { - return nil, err - } - - if key.KeyRangeContains(f.keyRange, ksid) { - rows = append(rows, row) - } - } - r.Rows = rows - return r, nil -} - -// reorderColumnsPrimaryKeyFirst returns a copy of "td" with the only difference -// that the Columns field is reordered such that the primary key columns come -// first. See orderedColumns() for details on the ordering. -func reorderColumnsPrimaryKeyFirst(td *tabletmanagerdatapb.TableDefinition) *tabletmanagerdatapb.TableDefinition { - reorderedTd := proto.Clone(td).(*tabletmanagerdatapb.TableDefinition) - reorderedTd.Columns = orderedColumns(td) - return reorderedTd -} - -// orderedColumns returns the list of columns: -// - first the primary key columns in the right order -// - then the rest of the columns -// Within the partition of non primary key columns, the order is not changed -// in comparison to the original order of "td.Columns". -func orderedColumns(td *tabletmanagerdatapb.TableDefinition) []string { - return orderedColumnsHelper(td, true) -} - -// orderedColumnsWithoutPrimaryKeyColumns is identical to orderedColumns but -// leaves the primary key columns out. -func orderedColumnsWithoutPrimaryKeyColumns(td *tabletmanagerdatapb.TableDefinition) []string { - return orderedColumnsHelper(td, false) -} - -func orderedColumnsHelper(td *tabletmanagerdatapb.TableDefinition, includePrimaryKey bool) []string { - var result []string - if includePrimaryKey { - result = append(result, td.PrimaryKeyColumns...) - } - for _, column := range td.Columns { - found := false - for _, primaryKey := range td.PrimaryKeyColumns { - if primaryKey == column { - found = true - break - } - } - if !found { - result = append(result, column) - } - } - return result -} - -// uint64FromKeyspaceID returns the 64 bit number representation -// of the keyspaceID. -func uint64FromKeyspaceID(keyspaceID []byte) uint64 { - // Copy into 8 bytes because keyspaceID could be shorter. - bits := make([]byte, 8) - copy(bits, keyspaceID) - return binary.BigEndian.Uint64(bits) -} - -// TableScan returns a QueryResultReader that gets all the rows from a -// table, ordered by Primary Key. The returned columns are ordered -// with the Primary Key columns in front. -func TableScan(ctx context.Context, log logutil.Logger, ts *topo.Server, tabletAlias *topodatapb.TabletAlias, td *tabletmanagerdatapb.TableDefinition) (*QueryResultReader, error) { - sql := fmt.Sprintf("SELECT %v FROM %v", strings.Join(escapeAll(orderedColumns(td)), ", "), sqlescape.EscapeID(td.Name)) - if len(td.PrimaryKeyColumns) > 0 { - sql += fmt.Sprintf(" ORDER BY %v", strings.Join(escapeAll(td.PrimaryKeyColumns), ", ")) - } - log.Infof("SQL query for %v/%v: %v", topoproto.TabletAliasString(tabletAlias), td.Name, sql) - return NewQueryResultReaderForTablet(ctx, ts, tabletAlias, sql) -} - -// TransactionalTableScan does the same thing as TableScan, but runs inside a transaction -func TransactionalTableScan(ctx context.Context, log logutil.Logger, ts *topo.Server, tabletAlias *topodatapb.TabletAlias, txID int64, td *tabletmanagerdatapb.TableDefinition) (*QueryResultReader, error) { - sql := fmt.Sprintf("SELECT %v FROM %v", strings.Join(escapeAll(orderedColumns(td)), ", "), sqlescape.EscapeID(td.Name)) - if len(td.PrimaryKeyColumns) > 0 { - sql += fmt.Sprintf(" ORDER BY %v", strings.Join(escapeAll(td.PrimaryKeyColumns), ", ")) - } - log.Infof("SQL query for %v/%v: %v", topoproto.TabletAliasString(tabletAlias), td.Name, sql) - return NewTransactionalQueryResultReaderForTablet(ctx, ts, tabletAlias, sql, txID) -} - -// CreateTargetFrom is a helper function -func CreateTargetFrom(tablet *topodatapb.Tablet) *query.Target { - return &query.Target{ - Cell: tablet.Alias.Cell, - Keyspace: tablet.Keyspace, - Shard: tablet.Shard, - TabletType: tablet.Type, - } -} - -// TableScanByKeyRange returns a QueryResultReader that gets all the -// rows from a table that match the supplied KeyRange, ordered by -// Primary Key. The returned columns are ordered with the Primary Key -// columns in front. -// If keyspaceSchema is passed in, we go into v3 mode, and we ask for all -// source data, and filter here. Otherwise we stick with v2 mode, where we can -// ask the source tablet to do the filtering. -func TableScanByKeyRange(ctx context.Context, log logutil.Logger, ts *topo.Server, tabletAlias *topodatapb.TabletAlias, td *tabletmanagerdatapb.TableDefinition, keyRange *topodatapb.KeyRange, keyspaceSchema *vindexes.KeyspaceSchema) (*QueryResultReader, error) { - if keyspaceSchema != nil { - // switch to v3 mode. - keyResolver, err := newV3ResolverFromColumnList(keyspaceSchema, td.Name, orderedColumns(td)) - if err != nil { - return nil, vterrors.Wrapf(err, "cannot resolve v3 sharding keys for table %v", td.Name) - } - - // full table scan - scan, err := TableScan(ctx, log, ts, tabletAlias, td) - if err != nil { - return nil, err - } - - // with extra filter - scan.output = &v3KeyRangeFilter{ - input: scan.output, - resolver: keyResolver.(*v3Resolver), - keyRange: keyRange, - } - return scan, nil - } - return nil, vterrors.New(vtrpc.Code_FAILED_PRECONDITION, "vschema should not be empty") -} - -// ErrStoppedRowReader is returned by RowReader.Next() when -// StopAfterCurrentResult() and it finished the current result. -var ErrStoppedRowReader = vterrors.New(vtrpc.Code_ABORTED, "RowReader won't advance to the next Result because StopAfterCurrentResult() was called") - -// RowReader returns individual rows from a ResultReader. -type RowReader struct { - resultReader ResultReader - currentResult *sqltypes.Result - currentIndex int - stopAfterCurrentResult bool -} - -// NewRowReader returns a RowReader based on the QueryResultReader -func NewRowReader(resultReader ResultReader) *RowReader { - return &RowReader{ - resultReader: resultReader, - } -} - -// Next will return: -// (row, nil) for the next row -// (nil, nil) for EOF -// (nil, error) if an error occurred -func (rr *RowReader) Next() ([]sqltypes.Value, error) { - for rr.currentResult == nil || rr.currentIndex == len(rr.currentResult.Rows) { - if rr.stopAfterCurrentResult { - return nil, ErrStoppedRowReader - } - - var err error - rr.currentResult, err = rr.resultReader.Next() - if err != nil { - if err != io.EOF { - return nil, err - } - return nil, nil - } - rr.currentIndex = 0 - } - row := rr.currentResult.Rows[rr.currentIndex] - rr.currentIndex++ - return row, nil -} - -// Fields returns the types for the rows -func (rr *RowReader) Fields() []*querypb.Field { - return rr.resultReader.Fields() -} - -// Drain will empty the RowReader and return how many rows we got -func (rr *RowReader) Drain() (int, error) { - count := 0 - for { - row, err := rr.Next() - if err != nil { - return 0, err - } - if row == nil { - return count, nil - } - count++ - } -} - -// StopAfterCurrentResult tells RowReader to keep returning rows in Next() -// until it has finished the current Result. Once there, Next() will always -// return the "StoppedRowReader" error. -// This is feature is necessary for an optimization where the underlying -// ResultReader is the last input in a merge and we want to switch from reading -// rows to reading Results. -func (rr *RowReader) StopAfterCurrentResult() { - rr.stopAfterCurrentResult = true -} - -// DiffReport has the stats for a diff job -type DiffReport struct { - // general stats - processedRows int - - // stats about the diff - matchingRows int - mismatchedRows int - extraRowsLeft int - extraRowsRight int - - // QPS variables and stats - startingTime time.Time - processingQPS int -} - -// HasDifferences returns true if the diff job recorded any difference -func (dr *DiffReport) HasDifferences() bool { - return dr.mismatchedRows > 0 || dr.extraRowsLeft > 0 || dr.extraRowsRight > 0 -} - -// ComputeQPS fills in processingQPS -func (dr *DiffReport) ComputeQPS() { - if dr.processedRows > 0 { - dr.processingQPS = int(time.Duration(dr.processedRows) * time.Second / time.Since(dr.startingTime)) - } -} - -func (dr *DiffReport) String() string { - return fmt.Sprintf("DiffReport{%v processed, %v matching, %v mismatched, %v extra left, %v extra right, %v q/s}", dr.processedRows, dr.matchingRows, dr.mismatchedRows, dr.extraRowsLeft, dr.extraRowsRight, dr.processingQPS) -} - -// RowsEqual returns the index of the first different column, or -1 if -// both rows are the same. -func RowsEqual(left, right []sqltypes.Value) int { - for i, l := range left { - if !bytes.Equal(l.Raw(), right[i].Raw()) { - return i - } - } - return -1 -} - -// CompareRows returns: -// -1 if left is smaller than right -// 0 if left and right are equal -// +1 if left is bigger than right -// It compares only up to and including the first "compareCount" columns of each -// row. -// TODO: This can panic if types for left and right don't match. -func CompareRows(fields []*querypb.Field, compareCount int, left, right []sqltypes.Value) (int, error) { - for i := 0; i < compareCount; i++ { - lv, _ := evalengine.ToNative(left[i]) - rv, _ := evalengine.ToNative(right[i]) - switch l := lv.(type) { - case int64: - r := rv.(int64) - if l < r { - return -1, nil - } else if l > r { - return 1, nil - } - case uint64: - r := rv.(uint64) - if l < r { - return -1, nil - } else if l > r { - return 1, nil - } - case float64: - r := rv.(float64) - if l < r { - return -1, nil - } else if l > r { - return 1, nil - } - case []byte: - r := rv.([]byte) - return bytes.Compare(l, r), nil - default: - return 0, vterrors.Errorf(vtrpc.Code_FAILED_PRECONDITION, "unsupported type %T returned by mysql.proto.Convert", l) - } - } - return 0, nil -} - -// RowDiffer will consume rows on both sides, and compare them. -// It assumes left and right are sorted by ascending primary key. -// it will record errors if extra rows exist on either side. -type RowDiffer struct { - left *RowReader - right *RowReader - tableDefinition *tabletmanagerdatapb.TableDefinition -} - -// NewRowDiffer returns a new RowDiffer -func NewRowDiffer(left, right ResultReader, tableDefinition *tabletmanagerdatapb.TableDefinition) (*RowDiffer, error) { - leftFields := left.Fields() - rightFields := right.Fields() - if len(leftFields) != len(rightFields) { - return nil, vterrors.Errorf(vtrpc.Code_FAILED_PRECONDITION, "[table=%v] Cannot diff inputs with different types", tableDefinition.Name) - } - for i, field := range leftFields { - if field.Type != rightFields[i].Type { - return nil, vterrors.Errorf(vtrpc.Code_FAILED_PRECONDITION, "[table=%v] Cannot diff inputs with different types: field %v types are %v and %v", tableDefinition.Name, i, field.Type, rightFields[i].Type) - } - } - return &RowDiffer{ - left: NewRowReader(left), - right: NewRowReader(right), - tableDefinition: tableDefinition, - }, nil -} - -// Go runs the diff. If there is no error, it will drain both sides. -// If an error occurs, it will just return it and stop. -func (rd *RowDiffer) Go(log logutil.Logger) (dr DiffReport, err error) { - - dr.startingTime = time.Now() - defer dr.ComputeQPS() - - var left []sqltypes.Value - var right []sqltypes.Value - advanceLeft := true - advanceRight := true - for { - if advanceLeft { - left, err = rd.left.Next() - if err != nil { - return - } - advanceLeft = false - } - if advanceRight { - right, err = rd.right.Next() - if err != nil { - return - } - advanceRight = false - } - dr.processedRows++ - if left == nil { - // no more rows from the left - if right == nil { - // no more rows from right either, we're done - return - } - - // drain right, update count - log.Errorf("Draining extra row(s) found on the right starting with: %v", right) - var count int - if count, err = rd.right.Drain(); err != nil { - return dr, err - } - dr.extraRowsRight += 1 + count - return - } - if right == nil { - // no more rows from the right - // we know we have rows from left, drain, update count - log.Errorf("Draining extra row(s) found on the left starting with: %v", left) - var count int - if count, err = rd.left.Drain(); err != nil { - return dr, err - } - dr.extraRowsLeft += 1 + count - return - } - - // we have both left and right, compare - f := RowsEqual(left, right) - if f == -1 { - // rows are the same, next - dr.matchingRows++ - advanceLeft = true - advanceRight = true - continue - } - - if f >= len(rd.tableDefinition.PrimaryKeyColumns) { - // rows have the same primary key, only content is different - if dr.mismatchedRows < 10 { - log.Errorf("[table=%v] Different content %v in same PK: %v != %v", rd.tableDefinition.Name, dr.mismatchedRows, left, right) - } - dr.mismatchedRows++ - advanceLeft = true - advanceRight = true - continue - } - - // have to find the 'smallest' row and advance it - c, err := CompareRows(rd.left.Fields(), len(rd.tableDefinition.PrimaryKeyColumns), left, right) - if err != nil { - return dr, err - } - if c < 0 { - if dr.extraRowsLeft < 10 { - log.Errorf("[table=%v] Extra row %v on left: %v", rd.tableDefinition.Name, dr.extraRowsLeft, left) - } - dr.extraRowsLeft++ - advanceLeft = true - continue - } else if c > 0 { - if dr.extraRowsRight < 10 { - log.Errorf("[table=%v] Extra row %v on right: %v", rd.tableDefinition.Name, dr.extraRowsRight, right) - } - dr.extraRowsRight++ - advanceRight = true - continue - } - - // After looking at primary keys more carefully, - // they're the same. Logging a regular difference - // then, and advancing both. - if dr.mismatchedRows < 10 { - log.Errorf("[table=%v] Different content %v in same PK: %v != %v", rd.tableDefinition.Name, dr.mismatchedRows, left, right) - } - dr.mismatchedRows++ - advanceLeft = true - advanceRight = true - } -} - -// createTransactions returns an array of transactions that all share the same view of the data. -// It will check that no new transactions have been seen between the creation of the underlying transactions, -// to guarantee that all TransactionalTableScanner are pointing to the same point -func createTransactions(ctx context.Context, numberOfScanners int, wr *wrangler.Wrangler, cleaner *wrangler.Cleaner, queryService queryservice.QueryService, target *query.Target, tabletInfo *topodatapb.Tablet) ([]int64, error) { - scanners := make([]int64, numberOfScanners) - for i := 0; i < numberOfScanners; i++ { - - tx, _, err := queryService.Begin(ctx, target, &query.ExecuteOptions{ - // Make sure our tx is not killed by tx sniper - Workload: query.ExecuteOptions_DBA, - TransactionIsolation: query.ExecuteOptions_CONSISTENT_SNAPSHOT_READ_ONLY, - }) - if err != nil { - return nil, vterrors.Wrapf(err, "could not open transaction on %v", topoproto.TabletAliasString(tabletInfo.Alias)) - } - - // Remember to rollback the transactions - cleaner.Record("CloseTransaction", topoproto.TabletAliasString(tabletInfo.Alias), func(ctx context.Context, wr *wrangler.Wrangler) error { - queryService, err := tabletconn.GetDialer()(tabletInfo, true) - if err != nil { - return err - } - _, err = queryService.Rollback(ctx, target, tx) - return err - }) - - scanners[i] = tx - } - - return scanners, nil -} - -// TableScanner is a simple abstraction that allows a TableScanner user to remain impervious -// by the transactionality of the connection -type TableScanner interface { - ScanTable(ctx context.Context, td *tabletmanagerdatapb.TableDefinition) (*QueryResultReader, error) -} - -// TransactionalTableScanner works inside of a transaction set up with CONSISTENT SNAPSHOT -type TransactionalTableScanner struct { - wr *wrangler.Wrangler - cleaner *wrangler.Cleaner - tabletAlias *topodatapb.TabletAlias - queryService queryservice.QueryService - tx int64 -} - -// ScanTable performs a full table scan, ordered by the primary keys, if any -func (tt TransactionalTableScanner) ScanTable(ctx context.Context, td *tabletmanagerdatapb.TableDefinition) (*QueryResultReader, error) { - return TransactionalTableScan(ctx, tt.wr.Logger(), tt.wr.TopoServer(), tt.tabletAlias, tt.tx, td) -} - -// NonTransactionalTableScanner just passes through the queries, and relies on paused replication traffic taking care of the consistent snapshot part -type NonTransactionalTableScanner struct { - wr *wrangler.Wrangler - cleaner *wrangler.Cleaner - tabletAlias *topodatapb.TabletAlias - queryService queryservice.QueryService -} - -// ScanTable performs a full table scan, ordered by the primary keys, if any -func (ntts NonTransactionalTableScanner) ScanTable(ctx context.Context, td *tabletmanagerdatapb.TableDefinition) (*QueryResultReader, error) { - return TableScan(ctx, ntts.wr.Logger(), ntts.wr.TopoServer(), ntts.tabletAlias, td) -} - -// CreateConsistentTableScanners will momentarily stop updates on the tablet, and then create connections that are all -// consistent snapshots of the same point in the transaction history -func CreateConsistentTableScanners(ctx context.Context, tablet *topo.TabletInfo, wr *wrangler.Wrangler, cleaner *wrangler.Cleaner, numberOfScanners int) ([]TableScanner, string, error) { - txs, gtid, err := CreateConsistentTransactions(ctx, tablet, wr, cleaner, numberOfScanners) - if err != nil { - return nil, "", err - } - - queryService, _ := tabletconn.GetDialer()(tablet.Tablet, true) - defer queryService.Close(ctx) - - scanners := make([]TableScanner, numberOfScanners) - for i, tx := range txs { - scanners[i] = TransactionalTableScanner{ - wr: wr, - cleaner: cleaner, - tabletAlias: tablet.Alias, - queryService: queryService, - tx: tx, - } - } - - return scanners, gtid, nil -} - -// CreateConsistentTransactions creates a number of consistent snapshot transactions, -// all starting from the same spot in the tx log -func CreateConsistentTransactions(ctx context.Context, tablet *topo.TabletInfo, wr *wrangler.Wrangler, cleaner *wrangler.Cleaner, numberOfScanners int) ([]int64, string, error) { - if numberOfScanners < 1 { - return nil, "", vterrors.Errorf(vtrpc.Code_INVALID_ARGUMENT, "need more than zero scanners: %d", numberOfScanners) - } - - tm := tmclient.NewTabletManagerClient() - defer tm.Close() - - // Lock all tables with a read lock to pause replication - err := tm.LockTables(ctx, tablet.Tablet) - if err != nil { - return nil, "", vterrors.Wrapf(err, "could not lock tables on %v", tablet.Tablet) - } - defer func() { - tm := tmclient.NewTabletManagerClient() - defer tm.Close() - tm.UnlockTables(ctx, tablet.Tablet) - wr.Logger().Infof("tables unlocked on %v", tablet.Tablet) - }() - - wr.Logger().Infof("tables locked on %v", tablet.Tablet) - target := CreateTargetFrom(tablet.Tablet) - - // Create transactions - queryService, _ := tabletconn.GetDialer()(tablet.Tablet, true) - defer queryService.Close(ctx) - connections, err := createTransactions(ctx, numberOfScanners, wr, cleaner, queryService, target, tablet.Tablet) - if err != nil { - return nil, "", vterrors.Wrapf(err, "failed to create transactions on %v", tablet.Tablet) - } - wr.Logger().Infof("transactions created on %v", topoproto.TabletAliasString(tablet.Tablet.Alias)) - executedGtid, err := tm.PrimaryPosition(ctx, tablet.Tablet) - if err != nil { - return nil, "", vterrors.Wrapf(err, "could not read executed GTID set on %v", tablet.Tablet) - } - - return connections, executedGtid, nil -} diff --git a/go/vt/worker/diff_utils_test.go b/go/vt/worker/diff_utils_test.go deleted file mode 100644 index 6d43bd1ff49..00000000000 --- a/go/vt/worker/diff_utils_test.go +++ /dev/null @@ -1,139 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package worker - -import ( - "encoding/hex" - "reflect" - "testing" - - "vitess.io/vitess/go/sqltypes" - querypb "vitess.io/vitess/go/vt/proto/query" - - tabletmanagerdatapb "vitess.io/vitess/go/vt/proto/tabletmanagerdata" -) - -func TestOrderedColumns(t *testing.T) { - input := &tabletmanagerdatapb.TableDefinition{ - PrimaryKeyColumns: []string{"pk1", "pk2"}, - Columns: []string{"pk1", "col1", "pk2", "col2"}, - } - want := []string{"pk1", "pk2", "col1", "col2"} - if got := orderedColumns(input); !reflect.DeepEqual(got, want) { - t.Errorf("got %v, want %v", got, want) - } -} - -func TestUint64FromKeyspaceId(t *testing.T) { - table := map[string]uint64{ - "10": 0x1000000000000000, - "fe": 0xfe00000000000000, - "1234cafe": 0x1234cafe00000000, - } - for input, want := range table { - keyspaceID, err := hex.DecodeString(input) - if err != nil { - t.Errorf("hex.DecodeString error: %v", err) - continue - } - if got := uint64FromKeyspaceID(keyspaceID); got != want { - t.Errorf("uint64FromKeyspaceID(%v) = %q, want %q", input, got, want) - } - } -} - -func TestCompareRows(t *testing.T) { - table := []struct { - fields []*querypb.Field - left, right []sqltypes.Value - want int - }{ - { - fields: []*querypb.Field{{Name: "a", Type: sqltypes.Int32}}, - left: []sqltypes.Value{sqltypes.NewInt32(123)}, - right: []sqltypes.Value{sqltypes.NewInt32(14)}, - want: 1, - }, - { - fields: []*querypb.Field{ - {Name: "a", Type: sqltypes.Int32}, - {Name: "b", Type: sqltypes.Int32}, - }, - left: []sqltypes.Value{ - sqltypes.NewInt32(555), - sqltypes.NewInt32(12), - }, - right: []sqltypes.Value{ - sqltypes.NewInt32(555), - sqltypes.NewInt32(144), - }, - want: -1, - }, - { - fields: []*querypb.Field{{Name: "a", Type: sqltypes.Int32}}, - left: []sqltypes.Value{sqltypes.NewInt32(144)}, - right: []sqltypes.Value{sqltypes.NewInt32(144)}, - want: 0, - }, - { - fields: []*querypb.Field{{Name: "a", Type: sqltypes.Uint64}}, - left: []sqltypes.Value{sqltypes.NewUint64(9223372036854775809)}, - right: []sqltypes.Value{sqltypes.NewUint64(9223372036854775810)}, - want: -1, - }, - { - fields: []*querypb.Field{{Name: "a", Type: sqltypes.Uint64}}, - left: []sqltypes.Value{sqltypes.NewUint64(9223372036854775819)}, - right: []sqltypes.Value{sqltypes.NewUint64(9223372036854775810)}, - want: 1, - }, - { - fields: []*querypb.Field{{Name: "a", Type: sqltypes.Float64}}, - left: []sqltypes.Value{sqltypes.NewFloat64(3.14)}, - right: []sqltypes.Value{sqltypes.NewFloat64(3.2)}, - want: -1, - }, - { - fields: []*querypb.Field{{Name: "a", Type: sqltypes.Float64}}, - left: []sqltypes.Value{sqltypes.NewFloat64(123.4)}, - right: []sqltypes.Value{sqltypes.NewFloat64(123.2)}, - want: 1, - }, - { - fields: []*querypb.Field{{Name: "a", Type: sqltypes.Char}}, - left: []sqltypes.Value{sqltypes.NewVarBinary("abc")}, - right: []sqltypes.Value{sqltypes.NewVarBinary("abb")}, - want: 1, - }, - { - fields: []*querypb.Field{{Name: "a", Type: sqltypes.Char}}, - left: []sqltypes.Value{sqltypes.NewVarBinary("abc")}, - right: []sqltypes.Value{sqltypes.NewVarBinary("abd")}, - want: -1, - }, - } - for _, tc := range table { - got, err := CompareRows(tc.fields, len(tc.fields), tc.left, tc.right) - if err != nil { - t.Errorf("CompareRows error: %v", err) - continue - } - if got != tc.want { - t.Errorf("CompareRows(%v, %v, %v) = %v, want %v", tc.fields, tc.left, tc.right, got, tc.want) - } - } -} diff --git a/go/vt/worker/events/split.go b/go/vt/worker/events/split.go deleted file mode 100644 index a96d6fe3ab1..00000000000 --- a/go/vt/worker/events/split.go +++ /dev/null @@ -1,39 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package events - -import ( - base "vitess.io/vitess/go/vt/events" -) - -// SplitClone is an event that describes a single step in a horizontal -// split clone. -type SplitClone struct { - base.StatusUpdater - - Keyspace, Shard, Cell string - ExcludeTables []string -} - -// VerticalSplitClone is an event that describes a single step in a vertical -// split clone. -type VerticalSplitClone struct { - base.StatusUpdater - - Keyspace, Shard, Cell string - Tables []string -} diff --git a/go/vt/worker/events/split_syslog.go b/go/vt/worker/events/split_syslog.go deleted file mode 100644 index 5703bd62fca..00000000000 --- a/go/vt/worker/events/split_syslog.go +++ /dev/null @@ -1,39 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package events - -import ( - "fmt" - "log/syslog" - - "vitess.io/vitess/go/event/syslogger" -) - -// Syslog writes a SplitClone event to syslog. -func (ev *SplitClone) Syslog() (syslog.Priority, string) { - return syslog.LOG_INFO, fmt.Sprintf("%s/%s/%s [split clone] %s", - ev.Keyspace, ev.Shard, ev.Cell, ev.Status) -} - -// Syslog writes a VerticalSplitClone event to syslog. -func (ev *VerticalSplitClone) Syslog() (syslog.Priority, string) { - return syslog.LOG_INFO, fmt.Sprintf("%s/%s/%s [vertical split clone] %s", - ev.Keyspace, ev.Shard, ev.Cell, ev.Status) -} - -var _ syslogger.Syslogger = (*SplitClone)(nil) // compile-time interface check -var _ syslogger.Syslogger = (*VerticalSplitClone)(nil) // compile-time interface check diff --git a/go/vt/worker/events/split_syslog_test.go b/go/vt/worker/events/split_syslog_test.go deleted file mode 100644 index 9e13c8ad19f..00000000000 --- a/go/vt/worker/events/split_syslog_test.go +++ /dev/null @@ -1,60 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package events - -import ( - "log/syslog" - "testing" - - base "vitess.io/vitess/go/vt/events" -) - -func TestSplitCloneSyslog(t *testing.T) { - wantSev, wantMsg := syslog.LOG_INFO, "keyspace-123/shard-123/cell-1 [split clone] status" - ev := &SplitClone{ - Cell: "cell-1", - Keyspace: "keyspace-123", - Shard: "shard-123", - StatusUpdater: base.StatusUpdater{Status: "status"}, - } - gotSev, gotMsg := ev.Syslog() - - if gotSev != wantSev { - t.Errorf("wrong severity: got %v, want %v", gotSev, wantSev) - } - if gotMsg != wantMsg { - t.Errorf("wrong message: got %v, want %v", gotMsg, wantMsg) - } -} - -func TestVerticalSplitCloneSyslog(t *testing.T) { - wantSev, wantMsg := syslog.LOG_INFO, "keyspace-123/shard-123/cell-1 [vertical split clone] status" - ev := &VerticalSplitClone{ - Cell: "cell-1", - Keyspace: "keyspace-123", - Shard: "shard-123", - StatusUpdater: base.StatusUpdater{Status: "status"}, - } - gotSev, gotMsg := ev.Syslog() - - if gotSev != wantSev { - t.Errorf("wrong severity: got %v, want %v", gotSev, wantSev) - } - if gotMsg != wantMsg { - t.Errorf("wrong message: got %v, want %v", gotMsg, wantMsg) - } -} diff --git a/go/vt/worker/executor.go b/go/vt/worker/executor.go deleted file mode 100644 index 2e2b30d5d8e..00000000000 --- a/go/vt/worker/executor.go +++ /dev/null @@ -1,248 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package worker - -import ( - "context" - "fmt" - "strconv" - "time" - - "vitess.io/vitess/go/sqltypes" - "vitess.io/vitess/go/vt/discovery" - "vitess.io/vitess/go/vt/throttler" - "vitess.io/vitess/go/vt/topo/topoproto" - "vitess.io/vitess/go/vt/vterrors" - "vitess.io/vitess/go/vt/wrangler" - - querypb "vitess.io/vitess/go/vt/proto/query" - topodatapb "vitess.io/vitess/go/vt/proto/topodata" -) - -// executor takes care of the write-side of the copy. -// There is one executor for each destination shard and writer thread. -// To-be-written data will be passed in through a channel. -// The main purpose of this struct is to aggregate the objects which won't -// change during the execution and remove them from method signatures. -// executor is also used for executing vreplication and RefreshState commands. -type executor struct { - wr *wrangler.Wrangler - tsc *discovery.LegacyTabletStatsCache - throttler *throttler.Throttler - keyspace string - shard string - threadID int - // statsKey is the cached metric key which we need when we increment the stats - // variable when we get throttled. - statsKey []string -} - -func newExecutor(wr *wrangler.Wrangler, tsc *discovery.LegacyTabletStatsCache, throttler *throttler.Throttler, keyspace, shard string, threadID int) *executor { - return &executor{ - wr: wr, - tsc: tsc, - throttler: throttler, - keyspace: keyspace, - shard: shard, - threadID: threadID, - statsKey: []string{keyspace, shard, strconv.FormatInt(int64(threadID), 10)}, - } -} - -// fetchLoop loops over the provided insertChannel and sends the commands to the -// current primary. -func (e *executor) fetchLoop(ctx context.Context, insertChannel chan string) error { - for { - select { - case cmd, ok := <-insertChannel: - if !ok { - // no more to read, we're done - return nil - } - if err := e.fetchWithRetries(ctx, func(ctx context.Context, tablet *topodatapb.Tablet) error { - _, err := e.wr.TabletManagerClient().ExecuteFetchAsApp(ctx, tablet, true, []byte(cmd), 0) - return err - }); err != nil { - return vterrors.Wrap(err, "ExecuteFetch failed") - } - case <-ctx.Done(): - // Doesn't really matter if this select gets starved, because the other case - // will also return an error due to executeFetch's context being closed. This case - // does prevent us from blocking indefinitely on insertChannel when the worker is canceled. - return nil - } - } -} - -func (e *executor) vreplicationExec(ctx context.Context, cmd string) (qr *sqltypes.Result, err error) { - var result *querypb.QueryResult - err = e.fetchWithRetries(ctx, func(ctx context.Context, tablet *topodatapb.Tablet) error { - var err error - result, err = e.wr.TabletManagerClient().VReplicationExec(ctx, tablet, cmd) - return err - }) - if err != nil { - return nil, err - } - return sqltypes.Proto3ToResult(result), err -} - -func (e *executor) refreshState(ctx context.Context) error { - return e.fetchWithRetries(ctx, func(ctx context.Context, tablet *topodatapb.Tablet) error { - return e.wr.TabletManagerClient().RefreshState(ctx, tablet) - }) -} - -// fetchWithRetries will attempt to run ExecuteFetch for a single command, with -// a reasonably small timeout. -// If will keep retrying the ExecuteFetch (for a finite but longer duration) if -// it fails due to a timeout or a retriable application error. -// -// executeFetchWithRetries will always get the current PRIMARY tablet from the -// LegacyTabletStatsCache instance. If no PRIMARY is available, it will keep retrying. -func (e *executor) fetchWithRetries(ctx context.Context, action func(ctx context.Context, tablet *topodatapb.Tablet) error) error { - retryDuration := *retryDuration - // We should keep retrying up until the retryCtx runs out. - retryCtx, retryCancel := context.WithTimeout(ctx, retryDuration) - defer retryCancel() - // Is this current attempt a retry of a previous attempt? - isRetry := false - for { - var primary *discovery.LegacyTabletStats - var err error - - // Get the current primary from the LegacyTabletStatsCache. - primaries := e.tsc.GetHealthyTabletStats(e.keyspace, e.shard, topodatapb.TabletType_PRIMARY) - if len(primaries) == 0 { - e.wr.Logger().Warningf("ExecuteFetch failed for keyspace/shard %v/%v because no PRIMARY is available; will retry until there is PRIMARY again", e.keyspace, e.shard) - statsRetryCount.Add(1) - statsRetryCounters.Add(retryCategoryNoPrimaryAvailable, 1) - goto retry - } - primary = &primaries[0] - - // Block if we are throttled. - if e.throttler != nil { - for { - backoff := e.throttler.Throttle(e.threadID) - if backoff == throttler.NotThrottled { - break - } - statsThrottledCounters.Add(e.statsKey, 1) - time.Sleep(backoff) - } - } - - // Run the command (in a block since goto above does not allow to introduce - // new variables until the label is reached.) - { - tryCtx, cancel := context.WithTimeout(retryCtx, 2*time.Minute) - err = action(tryCtx, primary.Tablet) - cancel() - - if err == nil { - // success! - return nil - } - - succeeded, finalErr := e.checkError(tryCtx, err, isRetry, primary) - if succeeded { - // We can ignore the error and don't have to retry. - return nil - } - if finalErr != nil { - // Non-retryable error. - return finalErr - } - } - - retry: - primaryAlias := "no-primary-was-available" - if primary != nil { - primaryAlias = topoproto.TabletAliasString(primary.Tablet.Alias) - } - tabletString := fmt.Sprintf("%v (%v/%v)", primaryAlias, e.keyspace, e.shard) - - select { - case <-retryCtx.Done(): - err := retryCtx.Err() - if err == context.DeadlineExceeded { - return vterrors.Wrapf(err, "failed to connect to destination tablet %v after retrying for %v", tabletString, retryDuration) - } - return vterrors.Wrapf(err, "interrupted while trying to run a command on tablet %v", tabletString) - case <-time.After(*executeFetchRetryTime): - // Retry 30s after the failure using the current primary seen by the LegacyHealthCheck. - } - isRetry = true - } -} - -// checkError returns true if the error can be ignored and the command -// succeeded, false if the error is retryable and a non-nil error if the -// command must not be retried. -func (e *executor) checkError(ctx context.Context, err error, isRetry bool, primary *discovery.LegacyTabletStats) (bool, error) { - tabletString := fmt.Sprintf("%v (%v/%v)", topoproto.TabletAliasString(primary.Tablet.Alias), e.keyspace, e.shard) - - // first see if it was a context timeout. - select { - case <-ctx.Done(): - if ctx.Err() == context.DeadlineExceeded { - e.wr.Logger().Warningf("ExecuteFetch failed on %v; will retry because it was a timeout error on the context", tabletString) - statsRetryCount.Add(1) - statsRetryCounters.Add(retryCategoryTimeoutError, 1) - return false, nil - } - default: - } - - // If the ExecuteFetch call failed because of an application error, we will try to figure out why. - // We need to extract the MySQL error number, and will attempt to retry if we think the error is recoverable. - match := errExtract.FindStringSubmatch(err.Error()) - var errNo string - if len(match) == 2 { - errNo = match[1] - } - switch { - case errNo == "1290": - e.wr.Logger().Warningf("ExecuteFetch failed on %v; will reresolve and retry because it's due to a MySQL read-only error: %v", tabletString, err) - statsRetryCount.Add(1) - statsRetryCounters.Add(retryCategoryReadOnly, 1) - case errNo == "2002" || errNo == "2006" || errNo == "2013" || errNo == "1053": - // Note: - // "2006" happens if the connection is already dead. Retrying a query in - // this case is safe. - // "2013" happens if the connection dies in the middle of a query. This is - // also safe to retry because either the query went through on the server or - // it was aborted. If we retry the query and get a duplicate entry error, we - // assume that the previous execution was successful and ignore the error. - // See below for the handling of duplicate entry error "1062". - // "1053" is mysql shutting down - e.wr.Logger().Warningf("ExecuteFetch failed on %v; will reresolve and retry because it's due to a MySQL connection error: %v", tabletString, err) - statsRetryCount.Add(1) - statsRetryCounters.Add(retryCategoryConnectionError, 1) - case errNo == "1062": - if !isRetry { - return false, vterrors.Wrapf(err, "ExecuteFetch failed on %v on the first attempt; not retrying as this is not a recoverable error", tabletString) - } - e.wr.Logger().Infof("ExecuteFetch failed on %v with a duplicate entry error; marking this as a success, because of the likelihood that this query has already succeeded before being retried: %v", tabletString, err) - return true, nil - default: - // Unknown error. - return false, err - } - return false, nil -} diff --git a/go/vt/worker/fakevtworkerclient/fakevtworkerclient.go b/go/vt/worker/fakevtworkerclient/fakevtworkerclient.go deleted file mode 100644 index 55a3f3d9265..00000000000 --- a/go/vt/worker/fakevtworkerclient/fakevtworkerclient.go +++ /dev/null @@ -1,59 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Package fakevtworkerclient contains a fake for the vtworkerclient interface. -package fakevtworkerclient - -import ( - "context" - - "vitess.io/vitess/go/vt/logutil" - "vitess.io/vitess/go/vt/vtctl/fakevtctlclient" - "vitess.io/vitess/go/vt/worker/vtworkerclient" -) - -// FakeVtworkerClient is a fake which implements the vtworkerclient interface. -// The fake can be used to return a specific result for a given command. -// If the command is not registered, an error will be thrown. -type FakeVtworkerClient struct { - *fakevtctlclient.FakeLoggerEventStreamingClient -} - -// NewFakeVtworkerClient creates a FakeVtworkerClient struct. -func NewFakeVtworkerClient() *FakeVtworkerClient { - return &FakeVtworkerClient{fakevtctlclient.NewFakeLoggerEventStreamingClient()} -} - -// FakeVtworkerClientFactory returns the current instance and stores the -// dialed server address in an outer struct. -func (f *FakeVtworkerClient) FakeVtworkerClientFactory(addr string) (vtworkerclient.Client, error) { - return &perAddrFakeVtworkerClient{f, addr}, nil -} - -// perAddrFakeVtworkerClient is a client instance which captures the server -// address which was dialed by the client. -type perAddrFakeVtworkerClient struct { - *FakeVtworkerClient - addr string -} - -// ExecuteVtworkerCommand is part of the vtworkerclient interface. -func (c *perAddrFakeVtworkerClient) ExecuteVtworkerCommand(ctx context.Context, args []string) (logutil.EventStream, error) { - return c.FakeLoggerEventStreamingClient.StreamResult(c.addr, args) -} - -// Close is part of the vtworkerclient interface. -func (c *perAddrFakeVtworkerClient) Close() {} diff --git a/go/vt/worker/grpcvtworkerclient/client.go b/go/vt/worker/grpcvtworkerclient/client.go deleted file mode 100644 index 3c082a236af..00000000000 --- a/go/vt/worker/grpcvtworkerclient/client.go +++ /dev/null @@ -1,101 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Package grpcvtworkerclient contains the gRPC version of the vtworker client protocol. -package grpcvtworkerclient - -import ( - "flag" - - "context" - - "google.golang.org/grpc" - - "vitess.io/vitess/go/vt/grpcclient" - "vitess.io/vitess/go/vt/logutil" - "vitess.io/vitess/go/vt/vterrors" - "vitess.io/vitess/go/vt/worker/vtworkerclient" - - logutilpb "vitess.io/vitess/go/vt/proto/logutil" - vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" - vtworkerdatapb "vitess.io/vitess/go/vt/proto/vtworkerdata" - vtworkerservicepb "vitess.io/vitess/go/vt/proto/vtworkerservice" -) - -var ( - cert = flag.String("vtworker_client_grpc_cert", "", "(DEPRECATED) the cert to use to connect") - key = flag.String("vtworker_client_grpc_key", "", "(DEPRECATED) the key to use to connect") - ca = flag.String("vtworker_client_grpc_ca", "", "(DEPRECATED) the server ca to use to validate servers when connecting") - crl = flag.String("vtworker_client_grpc_crl", "", "(DEPRECATED) the server crl to use to validate server certificates when connecting") - name = flag.String("vtworker_client_grpc_server_name", "", "(DEPRECATED) the server name to use to validate server certificate") -) - -type gRPCVtworkerClient struct { - cc *grpc.ClientConn - c vtworkerservicepb.VtworkerClient -} - -func gRPCVtworkerClientFactory(addr string) (vtworkerclient.Client, error) { - // create the RPC client - opt, err := grpcclient.SecureDialOption(*cert, *key, *ca, *crl, *name) - if err != nil { - return nil, err - } - cc, err := grpcclient.Dial(addr, grpcclient.FailFast(false), opt) - if err != nil { - return nil, vterrors.Errorf(vtrpcpb.Code_DEADLINE_EXCEEDED, "grpcclient.Dial() err: %v", err) - } - c := vtworkerservicepb.NewVtworkerClient(cc) - - return &gRPCVtworkerClient{ - cc: cc, - c: c, - }, nil -} - -type eventStreamAdapter struct { - stream vtworkerservicepb.Vtworker_ExecuteVtworkerCommandClient -} - -func (e *eventStreamAdapter) Recv() (*logutilpb.Event, error) { - le, err := e.stream.Recv() - if err != nil { - return nil, vterrors.FromGRPC(err) - } - return le.Event, nil -} - -// ExecuteVtworkerCommand is part of the VtworkerClient interface. -func (client *gRPCVtworkerClient) ExecuteVtworkerCommand(ctx context.Context, args []string) (logutil.EventStream, error) { - query := &vtworkerdatapb.ExecuteVtworkerCommandRequest{ - Args: args, - } - - stream, err := client.c.ExecuteVtworkerCommand(ctx, query) - if err != nil { - return nil, vterrors.FromGRPC(err) - } - return &eventStreamAdapter{stream}, nil -} - -// Close is part of the VtworkerClient interface. -func (client *gRPCVtworkerClient) Close() { - client.cc.Close() -} - -func init() { - vtworkerclient.RegisterFactory("grpc", gRPCVtworkerClientFactory) -} diff --git a/go/vt/worker/grpcvtworkerclient/client_test.go b/go/vt/worker/grpcvtworkerclient/client_test.go deleted file mode 100644 index 22ba809664d..00000000000 --- a/go/vt/worker/grpcvtworkerclient/client_test.go +++ /dev/null @@ -1,108 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package grpcvtworkerclient - -import ( - "flag" - "fmt" - "io" - "net" - "os" - "testing" - - "google.golang.org/grpc" - - "vitess.io/vitess/go/vt/servenv" - "vitess.io/vitess/go/vt/worker/grpcvtworkerserver" - "vitess.io/vitess/go/vt/worker/vtworkerclienttest" - - vtworkerservicepb "vitess.io/vitess/go/vt/proto/vtworkerservice" -) - -// Test gRPC interface using a vtworker and vtworkerclient. -func TestVtworkerServer(t *testing.T) { - wi := vtworkerclienttest.CreateWorkerInstance(t) - - // Listen on a random port. - listener, err := net.Listen("tcp", "127.0.0.1:0") - if err != nil { - t.Fatalf("Cannot listen: %v", err) - } - port := listener.Addr().(*net.TCPAddr).Port - - // Create a gRPC server and listen on the port. - server := grpc.NewServer() - vtworkerservicepb.RegisterVtworkerServer(server, grpcvtworkerserver.NewVtworkerServer(wi)) - go server.Serve(listener) - - // Create a VtworkerClient gRPC client to talk to the vtworker. - client, err := gRPCVtworkerClientFactory(fmt.Sprintf("localhost:%v", port)) - if err != nil { - t.Fatalf("Cannot create client: %v", err) - } - defer client.Close() - - vtworkerclienttest.TestSuite(t, client) -} - -// Test gRPC interface using a vtworker and vtworkerclient with auth. -func TestVtworkerServerAuth(t *testing.T) { - wi := vtworkerclienttest.CreateWorkerInstance(t) - - // Listen on a random port. - listener, err := net.Listen("tcp", "127.0.0.1:0") - if err != nil { - t.Fatalf("Cannot listen: %v", err) - } - port := listener.Addr().(*net.TCPAddr).Port - - // Create a gRPC server and listen on the port - var opts []grpc.ServerOption - opts = append(opts, grpc.StreamInterceptor(servenv.FakeAuthStreamInterceptor)) - opts = append(opts, grpc.UnaryInterceptor(servenv.FakeAuthUnaryInterceptor)) - server := grpc.NewServer(opts...) - - vtworkerservicepb.RegisterVtworkerServer(server, grpcvtworkerserver.NewVtworkerServer(wi)) - go server.Serve(listener) - - authJSON := `{ - "Username": "valid", - "Password": "valid" - }` - - f, err := os.CreateTemp("", "static_auth_creds.json") - if err != nil { - t.Fatal(err) - } - defer os.Remove(f.Name()) - if _, err := io.WriteString(f, authJSON); err != nil { - t.Fatal(err) - } - if err := f.Close(); err != nil { - t.Fatal(err) - } - - // Create a VtworkerClient gRPC client to talk to the vtworker. - flag.Set("grpc_auth_static_client_creds", f.Name()) - client, err := gRPCVtworkerClientFactory(fmt.Sprintf("localhost:%v", port)) - if err != nil { - t.Fatalf("Cannot create client: %v", err) - } - defer client.Close() - - vtworkerclienttest.TestSuite(t, client) -} diff --git a/go/vt/worker/grpcvtworkerserver/server.go b/go/vt/worker/grpcvtworkerserver/server.go deleted file mode 100644 index e4729e09db3..00000000000 --- a/go/vt/worker/grpcvtworkerserver/server.go +++ /dev/null @@ -1,87 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -/* -Package grpcvtworkerserver contains the gRPC implementation of the server side -of the remote execution of vtworker commands. -*/ -package grpcvtworkerserver - -import ( - "sync" - - "google.golang.org/grpc" - - "vitess.io/vitess/go/vt/logutil" - "vitess.io/vitess/go/vt/servenv" - "vitess.io/vitess/go/vt/vterrors" - "vitess.io/vitess/go/vt/worker" - - logutilpb "vitess.io/vitess/go/vt/proto/logutil" - vtworkerdatapb "vitess.io/vitess/go/vt/proto/vtworkerdata" - vtworkerservicepb "vitess.io/vitess/go/vt/proto/vtworkerservice" -) - -// VtworkerServer is our RPC server -type VtworkerServer struct { - vtworkerservicepb.UnimplementedVtworkerServer - wi *worker.Instance -} - -// NewVtworkerServer returns a new VtworkerServer for the given vtworker instance. -func NewVtworkerServer(wi *worker.Instance) *VtworkerServer { - return &VtworkerServer{wi: wi} -} - -// ExecuteVtworkerCommand is part of the vtworkerdatapb.VtworkerServer interface -func (s *VtworkerServer) ExecuteVtworkerCommand(args *vtworkerdatapb.ExecuteVtworkerCommandRequest, stream vtworkerservicepb.Vtworker_ExecuteVtworkerCommandServer) (err error) { - // Please note that this panic handler catches only panics occurring in the code below. - // The actual execution of the vtworker command takes place in a new go routine - // (started in Instance.setAndStartWorker()) which has its own panic handler. - defer servenv.HandlePanic("vtworker", &err) - - // Stream everything back what the Wrangler is logging. - // We may execute this in parallel (inside multiple go routines), - // but the stream.Send() method is not thread safe in gRPC. - // So use a mutex to protect it. - mu := sync.Mutex{} - logstream := logutil.NewCallbackLogger(func(e *logutilpb.Event) { - mu.Lock() - stream.Send(&vtworkerdatapb.ExecuteVtworkerCommandResponse{ - Event: e, - }) - mu.Unlock() - }) - // Let the Wrangler also log everything to the console (and thereby - // effectively to a logfile) to make sure that any information or errors - // is preserved in the logs in case the RPC or vtworker crashes. - logger := logutil.NewTeeLogger(logstream, logutil.NewConsoleLogger()) - - wr := s.wi.CreateWrangler(logger) - - // Run the command as long as the RPC Context is valid. - worker, done, err := s.wi.RunCommand(stream.Context(), args.Args, wr, false /*runFromCli*/) - if err == nil && worker != nil && done != nil { - err = s.wi.WaitForCommand(worker, done) - } - - return vterrors.ToGRPC(err) -} - -// StartServer registers the VtworkerServer for RPCs -func StartServer(s *grpc.Server, wi *worker.Instance) { - vtworkerservicepb.RegisterVtworkerServer(s, NewVtworkerServer(wi)) -} diff --git a/go/vt/worker/instance.go b/go/vt/worker/instance.go deleted file mode 100644 index e6fe0121556..00000000000 --- a/go/vt/worker/instance.go +++ /dev/null @@ -1,232 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package worker - -import ( - "os" - "os/signal" - "sync" - "syscall" - "time" - - "context" - - "vitess.io/vitess/go/trace" - - "vitess.io/vitess/go/tb" - "vitess.io/vitess/go/vt/log" - "vitess.io/vitess/go/vt/logutil" - vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" - "vitess.io/vitess/go/vt/topo" - "vitess.io/vitess/go/vt/vterrors" - "vitess.io/vitess/go/vt/vttablet/tmclient" - "vitess.io/vitess/go/vt/wrangler" -) - -// Instance encapsulate the execution state of vtworker. -type Instance struct { - // Default wrangler for all operations. - // Users can specify their own in RunCommand() e.g. the gRPC server does this. - wr *wrangler.Wrangler - - // mutex is protecting all the following variables - // 3 states here: - // - no job ever ran (or reset was run): currentWorker is nil, - // currentContext/currentCancelFunc is nil, lastRunError is nil - // - one worker running: currentWorker is set, - // currentContext/currentCancelFunc is set, lastRunError is nil - // - (at least) one worker already ran, none is running atm: - // currentWorker is set, currentContext is nil, lastRunError - // has the error returned by the worker. - currentWorkerMutex sync.Mutex - currentWorker Worker - currentMemoryLogger *logutil.MemoryLogger - currentContext context.Context - currentCancelFunc context.CancelFunc - lastRunError error - lastRunStopTime time.Time - - topoServer *topo.Server - cell string - commandDisplayInterval time.Duration -} - -// NewInstance creates a new Instance. -func NewInstance(ts *topo.Server, cell string, commandDisplayInterval time.Duration) *Instance { - wi := &Instance{topoServer: ts, cell: cell, commandDisplayInterval: commandDisplayInterval} - // Note: setAndStartWorker() also adds a MemoryLogger for the webserver. - wi.wr = wi.CreateWrangler(logutil.NewConsoleLogger()) - return wi -} - -// CreateWrangler creates a new wrangler using the instance specific configuration. -func (wi *Instance) CreateWrangler(logger logutil.Logger) *wrangler.Wrangler { - return wrangler.New(logger, wi.topoServer, tmclient.NewTabletManagerClient()) -} - -// setAndStartWorker will set the current worker. -// We always log to both memory logger (for display on the web) and -// console logger (for records / display of command line worker). -func (wi *Instance) setAndStartWorker(ctx context.Context, wrk Worker, wr *wrangler.Wrangler) (chan struct{}, error) { - wi.currentWorkerMutex.Lock() - defer wi.currentWorkerMutex.Unlock() - - if wi.currentContext != nil { - return nil, vterrors.Errorf(vtrpcpb.Code_UNAVAILABLE, "A worker job is already in progress: %v", wi.currentWorker.StatusAsText()) - } - - if wi.currentWorker != nil { - // During the grace period, we answer with a retryable error. - // This way any automation can retry to issue 'Reset' and then the original - // command. We can end up in this situation when the automation job was - // restarted and therefore the previously running vtworker command was - // canceled. - // TODO(mberlin): This can be simplified when we move to a model where - // vtworker runs commands independent of an RPC and the automation polls for - // the current status, based on an assigned unique id, instead. - const gracePeriod = 1 * time.Minute - sinceLastStop := time.Since(wi.lastRunStopTime) - if sinceLastStop <= gracePeriod { - return nil, vterrors.Errorf(vtrpcpb.Code_UNAVAILABLE, - "A worker job was recently stopped (%f seconds ago): If you run commands manually, run the 'Reset' command to clear the vtworker state. Job: %v", - sinceLastStop.Seconds(), - wi.currentWorker) - } - - // We return FAILED_PRECONDITION to signal that a manual resolution is required. - return nil, vterrors.Errorf(vtrpcpb.Code_FAILED_PRECONDITION, - "The worker job was stopped %.1f minutes ago, but not reset. Run the 'Reset' command to clear it manually. Job: %v", - time.Since(wi.lastRunStopTime).Minutes(), - wi.currentWorker) - } - - wi.currentWorker = wrk - wi.currentMemoryLogger = logutil.NewMemoryLogger() - wi.currentContext, wi.currentCancelFunc = context.WithCancel(ctx) - wi.lastRunError = nil - wi.lastRunStopTime = time.Unix(0, 0) - done := make(chan struct{}) - wranglerLogger := wr.Logger() - if wr == wi.wr { - // If it's the default wrangler, do not reuse its logger because it may have been set before. - // Resuing it would result into an endless recursion. - wranglerLogger = logutil.NewConsoleLogger() - } - wr.SetLogger(logutil.NewTeeLogger(wi.currentMemoryLogger, wranglerLogger)) - - // one go function runs the worker, changes state when done - go func() { - log.Infof("Starting worker...") - span, ctx := trace.NewSpan(wi.currentContext, "work") - span.Annotate("extra", wrk.State().String()) - defer span.Finish() - var err error - - // Catch all panics and always save the execution state at the end. - defer func() { - // The recovery code is a copy of servenv.HandlePanic(). - if x := recover(); x != nil { - log.Errorf("uncaught vtworker panic: %v\n%s", x, tb.Stack(4)) - err = vterrors.Errorf(vtrpcpb.Code_INTERNAL, "uncaught vtworker panic: %v", x) - } - - wi.currentWorkerMutex.Lock() - wi.currentContext = nil - wi.currentCancelFunc = nil - wi.lastRunError = err - wi.lastRunStopTime = time.Now() - wi.currentWorkerMutex.Unlock() - close(done) - }() - - // run will take a long time - err = wrk.Run(ctx) - - // If the context was canceled, include the respective error c ode. - select { - case <-ctx.Done(): - // Context is done i.e. probably canceled. - if wi.currentContext.Err() == context.Canceled { - err = vterrors.Errorf(vtrpcpb.Code_CANCELED, "vtworker command was canceled: %v", err) - } - default: - } - }() - - return done, nil -} - -// InstallSignalHandlers installs signal handler which exit vtworker gracefully. -func (wi *Instance) InstallSignalHandlers() { - sigChan := make(chan os.Signal, 1) - signal.Notify(sigChan, syscall.SIGTERM, syscall.SIGINT) - go func() { - for s := range sigChan { - // We got a signal, notify our modules. - // Use an extra function to properly unlock using defer. - func() { - wi.currentWorkerMutex.Lock() - defer wi.currentWorkerMutex.Unlock() - if wi.currentCancelFunc != nil { - log.Infof("Trying to cancel current worker after receiving signal: %v", s) - wi.currentCancelFunc() - } else { - log.Infof("Shutting down idle worker after receiving signal: %v", s) - os.Exit(0) - } - }() - } - }() -} - -// Reset resets the state of a finished worker. It returns an error if the worker is still running. -func (wi *Instance) Reset() error { - wi.currentWorkerMutex.Lock() - defer wi.currentWorkerMutex.Unlock() - - if wi.currentWorker == nil { - return nil - } - - // check the worker is really done - if wi.currentContext == nil { - wi.currentWorker = nil - wi.currentMemoryLogger = nil - return nil - } - - return vterrors.New(vtrpcpb.Code_FAILED_PRECONDITION, "worker still executing") -} - -// Cancel calls the cancel function of the current vtworker job. -// It returns true, if a job was running. False otherwise. -// NOTE: Cancel won't reset the state as well. Use Reset() to do so. -func (wi *Instance) Cancel() bool { - wi.currentWorkerMutex.Lock() - - if wi.currentWorker == nil || wi.currentCancelFunc == nil { - wi.currentWorkerMutex.Unlock() - return false - } - - cancel := wi.currentCancelFunc - wi.currentWorkerMutex.Unlock() - - cancel() - - return true -} diff --git a/go/vt/worker/interactive.go b/go/vt/worker/interactive.go deleted file mode 100644 index 1443b24f690..00000000000 --- a/go/vt/worker/interactive.go +++ /dev/null @@ -1,140 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package worker - -import ( - "fmt" - "html/template" - "net/http" - - "context" - - "vitess.io/vitess/go/acl" - "vitess.io/vitess/go/vt/log" - "vitess.io/vitess/go/vt/servenv" -) - -const indexHTML = ` - - - Worker Action Index - - -

Worker Action Index

- {{range $i, $group := . }} -
  • {{$group.Name}}: {{$group.Description}}
  • - {{end}} - -` - -const subIndexHTML = ` -{{$name := .Name}} - - - {{$name}} Index - - -

    {{$name}} Index

    -

    {{.Description}}

    - {{range $i, $cmd := .Commands }} -
  • {{$cmd.Name}}: {{$cmd.Help}}
  • - {{end}} - -` - -func httpError(w http.ResponseWriter, format string, args ...any) { - log.Errorf(format, args...) - http.Error(w, fmt.Sprintf(format, args...), http.StatusInternalServerError) -} - -func mustParseTemplate(name, contents string) *template.Template { - t, err := template.New(name).Parse(contents) - if err != nil { - // An invalid template here is a programming error. - panic(fmt.Sprintf("cannot parse %v template: %v", name, err)) - } - return t -} - -func executeTemplate(w http.ResponseWriter, t *template.Template, data any) { - if err := t.Execute(w, data); err != nil { - httpError(w, "error executing template: %v", err) - } -} - -// InitInteractiveMode installs webserver handlers for each known command. -func (wi *Instance) InitInteractiveMode() { - indexTemplate := mustParseTemplate("index", indexHTML) - subIndexTemplate := mustParseTemplate("subIndex", subIndexHTML) - - // toplevel menu - http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - if err := acl.CheckAccessHTTP(r, acl.ADMIN); err != nil { - acl.SendError(w, err) - return - } - - executeTemplate(w, indexTemplate, commands) - }) - - // command group menus - for _, cg := range commands { - // keep a local copy of the Command pointer for the - // closure. - pcg := cg - http.HandleFunc("/"+cg.Name, func(w http.ResponseWriter, r *http.Request) { - if err := acl.CheckAccessHTTP(r, acl.ADMIN); err != nil { - acl.SendError(w, err) - return - } - - executeTemplate(w, subIndexTemplate, pcg) - }) - - for _, c := range cg.Commands { - // keep a local copy of the Command pointer for the closure. - pc := c - http.HandleFunc("/"+cg.Name+"/"+c.Name, func(w http.ResponseWriter, r *http.Request) { - if err := acl.CheckAccessHTTP(r, acl.ADMIN); err != nil { - acl.SendError(w, err) - return - } - - wrk, template, data, err := pc.Interactive(context.Background(), wi, wi.wr, w, r) - if err != nil { - httpError(w, "%s", err) - } else if template != nil && data != nil { - executeTemplate(w, template, data) - return - } - - if wrk == nil { - httpError(w, "Internal server error. Command: %s did not return correct response.", c.Name) - return - } - - if _, err := wi.setAndStartWorker(context.Background(), wrk, wi.wr); err != nil { - httpError(w, "Could not set %s worker: %s", c.Name, err) - return - } - http.Redirect(w, r, servenv.StatusURLPath(), http.StatusTemporaryRedirect) - }) - } - } - - log.Infof("Interactive mode ready") -} diff --git a/go/vt/worker/key_resolver.go b/go/vt/worker/key_resolver.go deleted file mode 100644 index fe2c1790bd8..00000000000 --- a/go/vt/worker/key_resolver.go +++ /dev/null @@ -1,125 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package worker - -import ( - "context" - - "vitess.io/vitess/go/sqltypes" - "vitess.io/vitess/go/vt/proto/vtrpc" - "vitess.io/vitess/go/vt/vterrors" - - "vitess.io/vitess/go/vt/key" - "vitess.io/vitess/go/vt/mysqlctl/tmutils" - "vitess.io/vitess/go/vt/vtgate/vindexes" - - tabletmanagerdatapb "vitess.io/vitess/go/vt/proto/tabletmanagerdata" -) - -// This file defines the interface and implementations of sharding key resolvers. - -// keyspaceIDResolver defines the interface that needs to be satisfied to get a -// keyspace ID from a database row. -type keyspaceIDResolver interface { - // keyspaceID takes a table row, and returns the keyspace id as bytes. - // It will return an error if no sharding key can be found. - keyspaceID(row []sqltypes.Value) ([]byte, error) -} - -// v3Resolver is the keyspace id resolver that is used by VTGate V3 deployments. -// In V3, we use the VSchema to find a Unique VIndex of cost 0 or 1 for each -// table. -type v3Resolver struct { - shardingColumnIndex int - vindex vindexes.SingleColumn -} - -// newV3ResolverFromTableDefinition returns a keyspaceIDResolver for a v3 table. -func newV3ResolverFromTableDefinition(keyspaceSchema *vindexes.KeyspaceSchema, td *tabletmanagerdatapb.TableDefinition) (keyspaceIDResolver, error) { - if td.Type != tmutils.TableBaseTable { - return nil, vterrors.Errorf(vtrpc.Code_INVALID_ARGUMENT, "a keyspaceID resolver can only be created for a base table, got %v", td.Type) - } - tableSchema, ok := keyspaceSchema.Tables[td.Name] - if !ok { - return nil, vterrors.Errorf(vtrpc.Code_FAILED_PRECONDITION, "no vschema definition for table %v", td.Name) - } - // use the lowest cost unique vindex as the sharding key - colVindex, err := vindexes.FindVindexForSharding(td.Name, tableSchema.ColumnVindexes) - if err != nil { - return nil, err - } - - // Find the sharding key column index. - columnIndex, ok := tmutils.TableDefinitionGetColumn(td, colVindex.Columns[0].String()) - if !ok { - return nil, vterrors.Errorf(vtrpc.Code_FAILED_PRECONDITION, "table %v has a Vindex on unknown column %v", td.Name, colVindex.Columns[0]) - } - - return &v3Resolver{ - shardingColumnIndex: columnIndex, - // Only SingleColumn vindexes are returned by FindVindexForSharding. - vindex: colVindex.Vindex.(vindexes.SingleColumn), - }, nil -} - -// newV3ResolverFromColumnList returns a keyspaceIDResolver for a v3 table. -func newV3ResolverFromColumnList(keyspaceSchema *vindexes.KeyspaceSchema, name string, columns []string) (keyspaceIDResolver, error) { - tableSchema, ok := keyspaceSchema.Tables[name] - if !ok { - return nil, vterrors.Errorf(vtrpc.Code_FAILED_PRECONDITION, "no vschema definition for table %v", name) - } - // use the lowest cost unique vindex as the sharding key - colVindex, err := vindexes.FindVindexForSharding(name, tableSchema.ColumnVindexes) - if err != nil { - return nil, err - } - - // Find the sharding key column index. - columnIndex := -1 - for i, n := range columns { - if colVindex.Columns[0].EqualString(n) { - columnIndex = i - break - } - } - if columnIndex == -1 { - return nil, vterrors.Errorf(vtrpc.Code_FAILED_PRECONDITION, "table %v has a Vindex on unknown column %v", name, colVindex.Columns[0]) - } - - return &v3Resolver{ - shardingColumnIndex: columnIndex, - // Only SingleColumn vindexes are returned by FindVindexForSharding. - vindex: colVindex.Vindex.(vindexes.SingleColumn), - }, nil -} - -// keyspaceID implements the keyspaceIDResolver interface. -func (r *v3Resolver) keyspaceID(row []sqltypes.Value) ([]byte, error) { - v := row[r.shardingColumnIndex] - destinations, err := r.vindex.Map(context.TODO(), nil, []sqltypes.Value{v}) - if err != nil { - return nil, err - } - if len(destinations) != 1 { - return nil, vterrors.Errorf(vtrpc.Code_INTERNAL, "mapping row to keyspace id returned an invalid array of keyspace ids: %v", key.DestinationsString(destinations)) - } - ksid, ok := destinations[0].(key.DestinationKeyspaceID) - if !ok || len(ksid) == 0 { - return nil, vterrors.Errorf(vtrpc.Code_INTERNAL, "could not map %v to a keyspace id, got destination %v", v, destinations[0]) - } - return ksid, nil -} diff --git a/go/vt/worker/multi_split_diff.go b/go/vt/worker/multi_split_diff.go deleted file mode 100644 index cf5c84ef3bf..00000000000 --- a/go/vt/worker/multi_split_diff.go +++ /dev/null @@ -1,893 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package worker - -import ( - "context" - "fmt" - "html/template" - "sort" - "sync" - "time" - - "vitess.io/vitess/go/sqltypes" - "vitess.io/vitess/go/vt/binlog/binlogplayer" - "vitess.io/vitess/go/vt/concurrency" - "vitess.io/vitess/go/vt/mysqlctl/tmutils" - "vitess.io/vitess/go/vt/topo" - "vitess.io/vitess/go/vt/vtctl/schematools" - "vitess.io/vitess/go/vt/vterrors" - "vitess.io/vitess/go/vt/vtgate/vindexes" - "vitess.io/vitess/go/vt/vttablet/queryservice" - "vitess.io/vitess/go/vt/vttablet/tabletconn" - "vitess.io/vitess/go/vt/wrangler" - - tabletmanagerdatapb "vitess.io/vitess/go/vt/proto/tabletmanagerdata" - topodatapb "vitess.io/vitess/go/vt/proto/topodata" - "vitess.io/vitess/go/vt/proto/vtrpc" -) - -// Scanners encapsulates a source and a destination. We create one of these per parallel runner. -type Scanners struct { - // this is how we get data from the source shard - sourceScanner TableScanner - - // this is how we get data from the destination shards - we'll have one scanner per shard we are reading from - destinationScanner []TableScanner -} - -// MultiSplitDiffWorker executes a diff between a destination shard and its -// source shards in a shard split case. -type MultiSplitDiffWorker struct { - StatusWorker - - wr *wrangler.Wrangler - cell string - keyspace string - shard string - excludeTables []string - minHealthyTablets int - parallelDiffsCount int - waitForFixedTimeRatherThanGtidSet bool - cleaner *wrangler.Cleaner - useConsistentSnapshot bool - tabletType topodatapb.TabletType - - // populated during WorkerStateInit, read-only after that - keyspaceInfo *topo.KeyspaceInfo - shardInfo *topo.ShardInfo - sourceUID uint32 - destinationShards []*topo.ShardInfo - - // populated during WorkerStateFindTargets, read-only after that - sourceAlias *topodatapb.TabletAlias - destinationAliases []*topodatapb.TabletAlias // matches order of destinationShards - scanners []Scanners -} - -// NewMultiSplitDiffWorker returns a new MultiSplitDiffWorker object. -func NewMultiSplitDiffWorker(wr *wrangler.Wrangler, cell, keyspace, shard string, excludeTables []string, minHealthyTablets, parallelDiffsCount int, waitForFixedTimeRatherThanGtidSet bool, useConsistentSnapshot bool, tabletType topodatapb.TabletType) Worker { - return &MultiSplitDiffWorker{ - StatusWorker: NewStatusWorker(), - wr: wr, - cell: cell, - keyspace: keyspace, - shard: shard, - excludeTables: excludeTables, - minHealthyTablets: minHealthyTablets, - parallelDiffsCount: parallelDiffsCount, - cleaner: &wrangler.Cleaner{}, - useConsistentSnapshot: useConsistentSnapshot, - waitForFixedTimeRatherThanGtidSet: waitForFixedTimeRatherThanGtidSet, - tabletType: tabletType, - } -} - -// StatusAsHTML is part of the Worker interface -func (msdw *MultiSplitDiffWorker) StatusAsHTML() template.HTML { - state := msdw.State() - - result := "Working on: " + msdw.keyspace + "/" + msdw.shard + "
    \n" - result += "State: " + state.String() + "
    \n" - switch state { - case WorkerStateDiff: - result += "Running...
    \n" - case WorkerStateDiffWillFail: - result += "Running - have already found differences...\n" - case WorkerStateDone: - result += "Success.
    \n" - } - - return template.HTML(result) -} - -// StatusAsText is part of the Worker interface -func (msdw *MultiSplitDiffWorker) StatusAsText() string { - state := msdw.State() - - result := "Working on: " + msdw.keyspace + "/" + msdw.shard + "\n" - result += "State: " + state.String() + "\n" - switch state { - case WorkerStateDiff: - result += "Running...\n" - case WorkerStateDiffWillFail: - result += "Running - have already found differences...\n" - case WorkerStateDone: - result += "Success.\n" - } - return result -} - -// Run is mostly a wrapper to run the cleanup at the end. -func (msdw *MultiSplitDiffWorker) Run(ctx context.Context) error { - resetVars() - err := msdw.run(ctx) - - msdw.SetState(WorkerStateCleanUp) - cerr := msdw.cleaner.CleanUp(msdw.wr) - if cerr != nil { - if err != nil { - msdw.wr.Logger().Errorf("CleanUp failed in addition to job error: %v", cerr) - } else { - err = cerr - } - } - if err != nil { - msdw.wr.Logger().Errorf("Run() error: %v", err) - msdw.SetState(WorkerStateError) - return err - } - msdw.SetState(WorkerStateDone) - return nil -} - -func (msdw *MultiSplitDiffWorker) run(ctx context.Context) error { - // first state: read what we need to do - if err := msdw.init(ctx); err != nil { - return vterrors.Wrap(err, "init() failed") - } - if err := checkDone(ctx); err != nil { - return err - } - - // second state: find targets - if err := msdw.findTargets(ctx); err != nil { - return vterrors.Wrap(err, "findTargets() failed") - } - if err := checkDone(ctx); err != nil { - return err - } - - // third phase: synchronize replication - if err := msdw.synchronizeSrcAndDestTxState(ctx); err != nil { - return vterrors.Wrap(err, "synchronizeSrcAndDestTxState() failed") - } - if err := checkDone(ctx); err != nil { - return err - } - - // fourth phase: diff - if err := msdw.diff(ctx); err != nil { - return vterrors.Wrap(err, "diff() failed") - } - - return checkDone(ctx) -} - -// init phase: -// - read the shard info, make sure it has sources -func (msdw *MultiSplitDiffWorker) init(ctx context.Context) error { - msdw.SetState(WorkerStateInit) - - if msdw.useConsistentSnapshot { - msdw.wr.Logger().Infof("splitting using consistent snapshot") - } else { - msdw.wr.Logger().Infof("splitting using STOP SLAVE") - } - - var err error - shortCtx, cancel := context.WithTimeout(ctx, *remoteActionsTimeout) - msdw.keyspaceInfo, err = msdw.wr.TopoServer().GetKeyspace(shortCtx, msdw.keyspace) - cancel() - if err != nil { - return vterrors.Wrapf(err, "cannot read keyspace %v", msdw.keyspace) - } - shortCtx, cancel = context.WithTimeout(ctx, *remoteActionsTimeout) - msdw.shardInfo, err = msdw.wr.TopoServer().GetShard(shortCtx, msdw.keyspace, msdw.shard) - cancel() - if err != nil { - return vterrors.Wrapf(err, "cannot read shard %v/%v", msdw.keyspace, msdw.shard) - } - - if !msdw.shardInfo.HasPrimary() { - return vterrors.Errorf(vtrpc.Code_UNAVAILABLE, "shard %v/%v has no primary", msdw.keyspace, msdw.shard) - } - - destinationShards, err := msdw.findDestinationShards(ctx) - if err != nil { - return vterrors.Wrapf(err, "findDestinationShards() failed for %v/%v/%v", msdw.cell, msdw.keyspace, msdw.shard) - } - msdw.destinationShards = destinationShards - - return nil -} - -// findDestinationShards finds all the shards that have filtered replication from the source shard -func (msdw *MultiSplitDiffWorker) findDestinationShards(ctx context.Context) ([]*topo.ShardInfo, error) { - shortCtx, cancel := context.WithTimeout(ctx, *remoteActionsTimeout) - keyspaces, err := msdw.wr.TopoServer().GetKeyspaces(shortCtx) - cancel() - if err != nil { - return nil, vterrors.Wrap(err, "failed to get list of keyspaces") - } - - var resultArray []*topo.ShardInfo - - for _, keyspace := range keyspaces { - shardInfo, err := msdw.findShardsInKeyspace(ctx, keyspace) - if err != nil { - return nil, err - } - resultArray = append(resultArray, shardInfo...) - } - - if len(resultArray) == 0 { - return nil, vterrors.Errorf(vtrpc.Code_UNAVAILABLE, "there are no destination shards") - } - return resultArray, nil -} - -func (msdw *MultiSplitDiffWorker) findShardsInKeyspace(ctx context.Context, keyspace string) ([]*topo.ShardInfo, error) { - shortCtx, cancel := context.WithTimeout(ctx, *remoteActionsTimeout) - shards, err := msdw.wr.TopoServer().GetShardNames(shortCtx, keyspace) - cancel() - if err != nil { - return nil, vterrors.Wrapf(err, "failed to get list of shards for keyspace '%v'", keyspace) - } - - var resultArray []*topo.ShardInfo - first := true - - for _, shard := range shards { - shardInfo, uid, err := msdw.getShardInfo(ctx, keyspace, shard) - if err != nil { - return nil, err - } - // There might not be any source shards here - if shardInfo != nil { - if first { - msdw.sourceUID = uid - first = false - } else if msdw.sourceUID != uid { - return nil, vterrors.Errorf(vtrpc.Code_FAILED_PRECONDITION, "found a source ID that was different, aborting. %v vs %v", msdw.sourceUID, uid) - } - - resultArray = append(resultArray, shardInfo) - } - } - - return resultArray, nil -} - -func (msdw *MultiSplitDiffWorker) getShardInfo(ctx context.Context, keyspace string, shard string) (*topo.ShardInfo, uint32, error) { - shortCtx, cancel := context.WithTimeout(ctx, *remoteActionsTimeout) - si, err := msdw.wr.TopoServer().GetShard(shortCtx, keyspace, shard) - cancel() - if err != nil { - return nil, 0, vterrors.Wrap(err, "failed to get shard info from toposerver") - } - - for _, sourceShard := range si.SourceShards { - if len(sourceShard.Tables) == 0 && sourceShard.Keyspace == msdw.keyspace && sourceShard.Shard == msdw.shard { - // Prevents the same shard from showing up multiple times - return si, sourceShard.Uid, nil - } - } - - return nil, 0, nil -} - -// findTargets phase: -// - find one rdonly in source shard -// - find one rdonly per destination shard -// - mark them all as 'worker' pointing back to us -func (msdw *MultiSplitDiffWorker) findTargets(ctx context.Context) error { - msdw.SetState(WorkerStateFindTargets) - var err error - - var finderFunc func(keyspace string, shard string) (*topodatapb.TabletAlias, error) - if msdw.tabletType == topodatapb.TabletType_RDONLY { - finderFunc = func(keyspace string, shard string) (*topodatapb.TabletAlias, error) { - return FindWorkerTablet(ctx, msdw.wr, msdw.cleaner, nil /*tsc*/, msdw.cell, keyspace, shard, 1, topodatapb.TabletType_RDONLY) - } - } else { - finderFunc = func(keyspace string, shard string) (*topodatapb.TabletAlias, error) { - return FindHealthyTablet(ctx, msdw.wr, nil /*tsc*/, msdw.cell, keyspace, shard, 1, msdw.tabletType) - } - } - - msdw.sourceAlias, err = finderFunc(msdw.keyspace, msdw.shard) - if err != nil { - return vterrors.Wrapf(err, "finding source failed for %v/%v/%v", msdw.cell, msdw.keyspace, msdw.shard) - } - - msdw.destinationAliases = make([]*topodatapb.TabletAlias, len(msdw.destinationShards)) - for i, destinationShard := range msdw.destinationShards { - keyspace := destinationShard.Keyspace() - shard := destinationShard.ShardName() - destinationAlias, err := finderFunc(keyspace, shard) - if err != nil { - return vterrors.Wrapf(err, "finding destination failed for %v/%v/%v", msdw.cell, keyspace, shard) - } - msdw.destinationAliases[i] = destinationAlias - } - - return nil -} - -// ask the primary of the destination shard to pause filtered replication, -// and return the source binlog positions -// (add a cleanup task to restart filtered replication on primary) -func (msdw *MultiSplitDiffWorker) stopVreplicationOnAll(ctx context.Context, tabletInfo []*topo.TabletInfo) ([]string, error) { - destVreplicationPos := make([]string, len(msdw.destinationShards)) - - for i, shardInfo := range msdw.destinationShards { - tablet := tabletInfo[i].Tablet - - msdw.wr.Logger().Infof("stopping primary binlog replication on %v", shardInfo.PrimaryAlias) - shortCtx, cancel := context.WithTimeout(ctx, *remoteActionsTimeout) - _, err := msdw.wr.TabletManagerClient().VReplicationExec(shortCtx, tablet, binlogplayer.StopVReplication(msdw.sourceUID, "for split diff")) - cancel() - if err != nil { - return nil, vterrors.Wrapf(err, "VReplicationExec(stop) for %v failed", shardInfo.PrimaryAlias) - } - wrangler.RecordVReplicationAction(msdw.cleaner, tablet, binlogplayer.StartVReplication(msdw.sourceUID)) - shortCtx, cancel = context.WithTimeout(ctx, *remoteActionsTimeout) - p3qr, err := msdw.wr.TabletManagerClient().VReplicationExec(shortCtx, tablet, binlogplayer.ReadVReplicationPos(msdw.sourceUID)) - cancel() - if err != nil { - return nil, vterrors.Wrapf(err, "VReplicationExec(stop) for %v failed", msdw.shardInfo.PrimaryAlias) - } - qr := sqltypes.Proto3ToResult(p3qr) - if len(qr.Rows) != 1 || len(qr.Rows[0]) != 1 { - return nil, vterrors.Errorf(vtrpc.Code_FAILED_PRECONDITION, "unexpected result while reading position: %v", qr) - } - destVreplicationPos[i] = qr.Rows[0][0].ToString() - if err != nil { - return nil, vterrors.Wrapf(err, "StopBlp for %v failed", msdw.shardInfo.PrimaryAlias) - } - } - return destVreplicationPos, nil -} - -func (msdw *MultiSplitDiffWorker) getPrimaryTabletInfoForShard(ctx context.Context, shardInfo *topo.ShardInfo) (*topo.TabletInfo, error) { - shortCtx, cancel := context.WithTimeout(ctx, *remoteActionsTimeout) - primaryInfo, err := msdw.wr.TopoServer().GetTablet(shortCtx, shardInfo.PrimaryAlias) - cancel() - if err != nil { - return nil, vterrors.Wrapf(err, "synchronizeSrcAndDestTxState: cannot get Tablet record for primary %v", msdw.shardInfo.PrimaryAlias) - } - return primaryInfo, nil -} - -// stop the source tablet at a binlog position higher than the -// destination primaries. Return the reached position -// (add a cleanup task to restart binlog replication on the source tablet, and -// change the existing ChangeTabletType cleanup action to 'spare' type) -func (msdw *MultiSplitDiffWorker) stopReplicationOnSourceTabletAt(ctx context.Context, destVreplicationPos []string) (string, error) { - shortCtx, cancel := context.WithTimeout(ctx, *remoteActionsTimeout) - sourceTablet, err := msdw.wr.TopoServer().GetTablet(shortCtx, msdw.sourceAlias) - cancel() - if err != nil { - return "", err - } - - var mysqlPos string // will be the last GTID that we stopped at - for _, vreplicationPos := range destVreplicationPos { - // We need to stop the source RDONLY tablet at a position which includes ALL of the positions of the destination - // shards. We do this by starting replication and then stopping at a minimum of each blp position separately. - // TODO this is not terribly efficient but it's possible to implement without changing the existing RPC, - // if we make StopReplicationMinimum take multiple blp positions then this will be a lot more efficient because you just - // check for each position using WAIT_UNTIL_SQL_THREAD_AFTER_GTIDS and then stop replication. - - msdw.wr.Logger().Infof("stopping Replication %v at a minimum of %v", msdw.sourceAlias, vreplicationPos) - - shortCtx, cancel = context.WithTimeout(ctx, *remoteActionsTimeout) - msdw.wr.StartReplication(shortCtx, sourceTablet.Tablet) - cancel() - if err != nil { - return "", err - } - - shortCtx, cancel = context.WithTimeout(ctx, *remoteActionsTimeout) - mysqlPos, err = msdw.wr.TabletManagerClient().StopReplicationMinimum(shortCtx, sourceTablet.Tablet, vreplicationPos, *remoteActionsTimeout) - cancel() - if err != nil { - return "", vterrors.Wrapf(err, "cannot stop replication %v at right binlog position %v", msdw.sourceAlias, vreplicationPos) - } - } - // change the cleaner actions from ChangeTabletType(rdonly) - // to StartReplication() + ChangeTabletType(spare) - wrangler.RecordStartReplicationAction(msdw.cleaner, sourceTablet.Tablet) - - return mysqlPos, nil -} - -// ask the primary of the destination shard to resume filtered replication -// up to the specified source position, and return the destination position. -func (msdw *MultiSplitDiffWorker) stopVreplicationAt(ctx context.Context, shardInfo *topo.ShardInfo, sourcePosition string, primaryInfo *topo.TabletInfo) (string, error) { - msdw.wr.Logger().Infof("Restarting primary %v until it catches up to %v", shardInfo.PrimaryAlias, sourcePosition) - shortCtx, cancel := context.WithTimeout(ctx, *remoteActionsTimeout) - _, err := msdw.wr.TabletManagerClient().VReplicationExec(shortCtx, primaryInfo.Tablet, binlogplayer.StartVReplicationUntil(msdw.sourceUID, sourcePosition)) - cancel() - if err != nil { - return "", vterrors.Wrapf(err, "VReplication(start until) for %v until %v failed", shardInfo.PrimaryAlias, sourcePosition) - } - - shortCtx, cancel = context.WithTimeout(ctx, *remoteActionsTimeout) - err = msdw.wr.TabletManagerClient().VReplicationWaitForPos(shortCtx, primaryInfo.Tablet, int(msdw.sourceUID), sourcePosition) - cancel() - if err != nil { - return "", vterrors.Wrapf(err, "VReplicationWaitForPos for %v until %v failed", shardInfo.PrimaryAlias, sourcePosition) - } - - shortCtx, cancel = context.WithTimeout(ctx, *remoteActionsTimeout) - primaryPos, err := msdw.wr.TabletManagerClient().PrimaryPosition(shortCtx, primaryInfo.Tablet) - cancel() - if err != nil { - return "", vterrors.Wrapf(err, "PrimaryPosition for %v failed", msdw.shardInfo.PrimaryAlias) - } - return primaryPos, nil -} - -// wait until the destination tablet is equal or passed that primary -// binlog position, and stop its replication. -// (add a cleanup task to restart binlog replication on it, and change -// the existing ChangeTabletType cleanup action to 'spare' type) -func (msdw *MultiSplitDiffWorker) stopReplicationAt(ctx context.Context, destinationAlias *topodatapb.TabletAlias, primaryPos string) error { - if msdw.waitForFixedTimeRatherThanGtidSet { - msdw.wr.Logger().Infof("workaround for broken GTID set in destination RDONLY. Just waiting for 1 minute for %v and assuming replication has caught up. (should be at %v)", destinationAlias, primaryPos) - } else { - msdw.wr.Logger().Infof("waiting for destination tablet %v to catch up to %v", destinationAlias, primaryPos) - } - shortCtx, cancel := context.WithTimeout(ctx, *remoteActionsTimeout) - destinationTablet, err := msdw.wr.TopoServer().GetTablet(shortCtx, destinationAlias) - cancel() - if err != nil { - return err - } - - if msdw.waitForFixedTimeRatherThanGtidSet { - time.Sleep(1 * time.Minute) - } - - shortCtx, cancel = context.WithTimeout(ctx, *remoteActionsTimeout) - if msdw.waitForFixedTimeRatherThanGtidSet { - err = msdw.wr.TabletManagerClient().StopReplication(shortCtx, destinationTablet.Tablet) - } else { - _, err = msdw.wr.TabletManagerClient().StopReplicationMinimum(shortCtx, destinationTablet.Tablet, primaryPos, *remoteActionsTimeout) - } - cancel() - if err != nil { - return vterrors.Wrapf(err, "StopReplicationMinimum for %v at %v failed", destinationAlias, primaryPos) - } - wrangler.RecordStartReplicationAction(msdw.cleaner, destinationTablet.Tablet) - return nil -} - -// restart filtered replication on the destination primary. -// (remove the cleanup task that does the same) -func (msdw *MultiSplitDiffWorker) startVreplication(ctx context.Context, shardInfo *topo.ShardInfo, primaryInfo *topo.TabletInfo) error { - msdw.wr.Logger().Infof("restarting filtered replication on primary %v", shardInfo.PrimaryAlias) - shortCtx, cancel := context.WithTimeout(ctx, *remoteActionsTimeout) - _, err := msdw.wr.TabletManagerClient().VReplicationExec(shortCtx, primaryInfo.Tablet, binlogplayer.StartVReplication(msdw.sourceUID)) - if err != nil { - cancel() - return vterrors.Wrapf(err, "VReplicationExec(start) failed for %v", shardInfo.PrimaryAlias) - } - cancel() - return nil -} - -func (msdw *MultiSplitDiffWorker) createNonTransactionalTableScanners(ctx context.Context, queryService queryservice.QueryService, source *topo.TabletInfo) ([]TableScanner, error) { - // If we are not using consistent snapshot, we'll use the NonTransactionalTableScanner, - // which does not have any instance state and so can be used by all connections - scanners := make([]TableScanner, msdw.parallelDiffsCount) - scanner := NonTransactionalTableScanner{ - queryService: queryService, - cleaner: msdw.cleaner, - wr: msdw.wr, - tabletAlias: source.Alias, - } - - for i := 0; i < msdw.parallelDiffsCount; i++ { - scanners[i] = scanner - } - - return scanners, nil -} - -func (msdw *MultiSplitDiffWorker) useTransactionScanners(ctx context.Context, source *topo.TabletInfo) (string, []TableScanner, error) { - connections, pos, err := CreateConsistentTableScanners(ctx, source, msdw.wr, msdw.cleaner, msdw.parallelDiffsCount) - if err != nil { - return "", nil, vterrors.Wrapf(err, "failed to create %d transactional connections", msdw.parallelDiffsCount) - } - return pos, connections, nil -} -func (msdw *MultiSplitDiffWorker) useNonTransactionalScanners(ctx context.Context, source *topo.TabletInfo, destVreplicationPos []string) (string, []TableScanner, error) { - pos, err := msdw.stopReplicationOnSourceTabletAt(ctx, destVreplicationPos) - if err != nil { - return "", nil, err - } - - queryService, err := tabletconn.GetDialer()(source.Tablet, true) - if err != nil { - return "", nil, vterrors.Wrapf(err, "failed to instantiate query service for %v", source.Tablet) - } - sourceScanners, err := msdw.createNonTransactionalTableScanners(ctx, queryService, source) - if err != nil { - return "", nil, err - } - - return pos, sourceScanners, nil -} - -// synchronizeSrcAndDestTxState phase: -// After this point, the source and the destination tablet are stopped at the same point. -func (msdw *MultiSplitDiffWorker) synchronizeSrcAndDestTxState(ctx context.Context) error { - msdw.SetState(WorkerStateSyncReplication) - var err error - - // 1. Find all the tablets we will need to work with - primaryInfos := make([]*topo.TabletInfo, len(msdw.destinationAliases)) - for i, shardInfo := range msdw.destinationShards { - primaryInfos[i], err = msdw.getPrimaryTabletInfoForShard(ctx, shardInfo) - if err != nil { - return err - } - } - - shortCtx, cancel := context.WithTimeout(ctx, *remoteActionsTimeout) - source, _ := msdw.wr.TopoServer().GetTablet(shortCtx, msdw.sourceAlias) - cancel() - - var sourcePosition string - - // 2. Stop replication on destination - destVreplicationPos, err := msdw.stopVreplicationOnAll(ctx, primaryInfos) - if err != nil { - return err - } - - // 3. Pause updates on the source and create consistent snapshot connections - var scanners []TableScanner - if msdw.useConsistentSnapshot { - sourcePosition, scanners, err = msdw.useTransactionScanners(ctx, source) - } else { - sourcePosition, scanners, err = msdw.useNonTransactionalScanners(ctx, source, destVreplicationPos) - } - if err != nil { - return err - } - - msdw.scanners = make([]Scanners, msdw.parallelDiffsCount) - for i := 0; i < msdw.parallelDiffsCount; i++ { - // We'll create one of these Scanners struct per thread we want to run in parallel - scanner := scanners[i] - i2 := Scanners{ - sourceScanner: scanner, - destinationScanner: make([]TableScanner, len(msdw.destinationShards)), - } - msdw.scanners[i] = i2 - } - - // 4. Make sure all replicas have caught up with the primary - for i, shardInfo := range msdw.destinationShards { - primaryInfo := primaryInfos[i] - destinationAlias := msdw.destinationAliases[i] - - destinationPosition, err := msdw.stopVreplicationAt(ctx, shardInfo, sourcePosition, primaryInfo) - if err != nil { - return err - } - - shortCtx, cancel := context.WithTimeout(ctx, *remoteActionsTimeout) - destTabletInfo, err := msdw.wr.TopoServer().GetTablet(shortCtx, destinationAlias) - cancel() - if err != nil { - return vterrors.Wrapf(err, "waitForDestinationTabletToReach: cannot get Tablet record for primary %v", msdw.shardInfo.PrimaryAlias) - } - - queryService, _ := tabletconn.GetDialer()(source.Tablet, true) - var destScanners []TableScanner - - if msdw.useConsistentSnapshot { - // loop to wait for the destinationAlias tablet in shardInfo to have reached destinationPosition - err = msdw.waitForDestinationTabletToReach(ctx, destTabletInfo.Tablet, destinationPosition) - if err != nil { - return err - } - - destScanners, _, err = CreateConsistentTableScanners(ctx, destTabletInfo, msdw.wr, msdw.cleaner, msdw.parallelDiffsCount) - if err != nil { - return err - } - } else { - err = msdw.stopReplicationAt(ctx, destinationAlias, destinationPosition) - if err != nil { - return vterrors.Wrapf(err, "failed to stop replication on %v at position %v", destinationAlias, destinationPosition) - } - destScanners, err = msdw.createNonTransactionalTableScanners(ctx, queryService, destTabletInfo) - if err != nil { - return vterrors.Wrapf(err, "failed to stop create table scanners for %v using %v", destTabletInfo, queryService) - } - } - - // 5. Spread out destination scanners between the goroutines - for j := 0; j < msdw.parallelDiffsCount; j++ { - msdw.scanners[j].destinationScanner[i] = destScanners[j] - } - - err = msdw.startVreplication(ctx, shardInfo, primaryInfo) - if err != nil { - return vterrors.Wrapf(err, "failed to restart vreplication for shard %v on tablet %v", shardInfo, primaryInfo) - } - - } - return nil -} - -func (msdw *MultiSplitDiffWorker) waitForDestinationTabletToReach(ctx context.Context, tablet *topodatapb.Tablet, mysqlPos string) error { - for i := 0; i < 20; i++ { - shortCtx, cancel := context.WithTimeout(ctx, *remoteActionsTimeout) - pos, err := msdw.wr.TabletManagerClient().PrimaryPosition(shortCtx, tablet) - cancel() - if err != nil { - return vterrors.Wrapf(err, "get PrimaryPosition for %v failed", tablet) - } - - if pos == mysqlPos { - return nil - } - time.Sleep(time.Second) - } - return vterrors.Errorf(vtrpc.Code_UNAVAILABLE, "failed to reach transaction position after multiple attempts. it is safe to try again") -} - -func (msdw *MultiSplitDiffWorker) diffSingleTable(ctx context.Context, wg *sync.WaitGroup, tableDefinition *tabletmanagerdatapb.TableDefinition, keyspaceSchema *vindexes.KeyspaceSchema, sourceScanner TableScanner, destinationScanners []TableScanner) error { - msdw.wr.Logger().Infof("Starting the diff on table %v", tableDefinition.Name) - - if len(destinationScanners) != len(msdw.destinationAliases) { - return vterrors.Errorf(vtrpc.Code_FAILED_PRECONDITION, "did not receive the expected amount of destination connections") - } - - sourceQueryResultReader, err := sourceScanner.ScanTable(ctx, tableDefinition) - if err != nil { - return err - } - defer sourceQueryResultReader.Close(ctx) - - destinationQueryResultReaders := make([]ResultReader, len(msdw.destinationAliases)) - for i := range msdw.destinationAliases { - scanner := destinationScanners[i] - destinationQueryResultReader, err := scanner.ScanTable(ctx, tableDefinition) - if err != nil { - return vterrors.Wrapf(err, "TableScan(destination) on %v failed", tableDefinition.String()) - } - - // For the first result scanner, let's check the PKs are of types that we can work with - if i == 0 { - err = CheckValidTypesForResultMerger(destinationQueryResultReader.fields, len(tableDefinition.PrimaryKeyColumns)) - if err != nil { - return vterrors.Wrapf(err, "invalid types for multi split diff. use the regular split diff instead") - } - } - - // We are knowingly using defer inside the for loop. - // All these readers need to be active until the diff is done - //noinspection GoDeferInLoop - defer destinationQueryResultReader.Close(ctx) - destinationQueryResultReaders[i] = destinationQueryResultReader - } - mergedResultReader, err := NewResultMerger(destinationQueryResultReaders, len(tableDefinition.PrimaryKeyColumns)) - if err != nil { - return err - } - - // Create the row differ. - differ, err := NewRowDiffer(sourceQueryResultReader, mergedResultReader, tableDefinition) - if err != nil { - return err - } - - // And run the diff. - report, err := differ.Go(msdw.wr.Logger()) - if err != nil { - return err - } - - if report.HasDifferences() { - return vterrors.Errorf(vtrpc.Code_FAILED_PRECONDITION, "table %v has differences: %v", tableDefinition.Name, report.String()) - } - - msdw.wr.Logger().Infof("table %v checks out (%v rows processed, %v qps)", tableDefinition.Name, report.processedRows, report.processingQPS) - - return nil -} - -func (msdw *MultiSplitDiffWorker) tableDiffingConsumer(ctx context.Context, wg *sync.WaitGroup, tableChan chan *tabletmanagerdatapb.TableDefinition, rec *concurrency.AllErrorRecorder, keyspaceSchema *vindexes.KeyspaceSchema, sourceScanner TableScanner, destinationScanners []TableScanner) { - if destinationScanners == nil || sourceScanner == nil { - err := vterrors.Errorf(vtrpc.Code_INTERNAL, "should not be nil %v %v", destinationScanners, sourceScanner) - msdw.markAsWillFail(rec, err) - msdw.wr.Logger().Errorf("%v", err) - return - } - defer wg.Done() - - for tableDefinition := range tableChan { - err := msdw.diffSingleTable(ctx, wg, tableDefinition, keyspaceSchema, sourceScanner, destinationScanners) - if err != nil { - msdw.markAsWillFail(rec, err) - msdw.wr.Logger().Errorf("%v", err) - } - } -} - -func (msdw *MultiSplitDiffWorker) gatherSchemaInfo(ctx context.Context) ([]*tabletmanagerdatapb.SchemaDefinition, *tabletmanagerdatapb.SchemaDefinition, error) { - msdw.wr.Logger().Infof("gathering schema information...") - wg := sync.WaitGroup{} - rec := &concurrency.AllErrorRecorder{} - - // this array will have concurrent writes to it, but no two goroutines will write to the same slot in the array - destinationSchemaDefinitions := make([]*tabletmanagerdatapb.SchemaDefinition, len(msdw.destinationAliases)) - var sourceSchemaDefinition *tabletmanagerdatapb.SchemaDefinition - for i, destinationAlias := range msdw.destinationAliases { - wg.Add(1) - go func(i int, destinationAlias *topodatapb.TabletAlias) { - var err error - shortCtx, cancel := context.WithTimeout(ctx, *remoteActionsTimeout) - req := &tabletmanagerdatapb.GetSchemaRequest{ExcludeTables: msdw.excludeTables} - destinationSchemaDefinition, err := schematools.GetSchema( - shortCtx, msdw.wr.TopoServer(), msdw.wr.TabletManagerClient(), destinationAlias, req) - cancel() - if err != nil { - msdw.markAsWillFail(rec, err) - } - destinationSchemaDefinitions[i] = destinationSchemaDefinition - msdw.wr.Logger().Infof("got schema from destination %v", destinationAlias) - wg.Done() - }(i, destinationAlias) - } - wg.Add(1) - go func() { - var err error - shortCtx, cancel := context.WithTimeout(ctx, *remoteActionsTimeout) - req := &tabletmanagerdatapb.GetSchemaRequest{ExcludeTables: msdw.excludeTables} - sourceSchemaDefinition, err = schematools.GetSchema( - shortCtx, msdw.wr.TopoServer(), msdw.wr.TabletManagerClient(), msdw.sourceAlias, req) - cancel() - if err != nil { - msdw.markAsWillFail(rec, err) - } - msdw.wr.Logger().Infof("got schema from source %v", msdw.sourceAlias) - wg.Done() - }() - - wg.Wait() - if rec.HasErrors() { - return nil, nil, rec.Error() - } - - return destinationSchemaDefinitions, sourceSchemaDefinition, nil -} - -func (msdw *MultiSplitDiffWorker) diffSchemaInformation(ctx context.Context, destinationSchemaDefinitions []*tabletmanagerdatapb.SchemaDefinition, sourceSchemaDefinition *tabletmanagerdatapb.SchemaDefinition) error { - msdw.wr.Logger().Infof("diffing the schema...") - rec := &concurrency.AllErrorRecorder{} - sourceShardName := fmt.Sprintf("%v/%v", msdw.shardInfo.Keyspace(), msdw.shardInfo.ShardName()) - for i, destinationSchemaDefinition := range destinationSchemaDefinitions { - destinationShard := msdw.destinationShards[i] - destinationShardName := fmt.Sprintf("%v/%v", destinationShard.Keyspace(), destinationShard.ShardName()) - tmutils.DiffSchema(destinationShardName, destinationSchemaDefinition, sourceShardName, sourceSchemaDefinition, rec) - } - if rec.HasErrors() { - msdw.wr.Logger().Warningf("different schemas: %v", rec.Error().Error()) - return rec.Error() - } - - msdw.wr.Logger().Infof("schema match, good.") - return nil -} - -func (msdw *MultiSplitDiffWorker) loadVSchema(ctx context.Context) (*vindexes.KeyspaceSchema, error) { - shortCtx, cancel := context.WithCancel(ctx) - kschema, err := msdw.wr.TopoServer().GetVSchema(shortCtx, msdw.keyspace) - cancel() - if err != nil { - return nil, vterrors.Wrapf(err, "cannot load VSchema for keyspace %v", msdw.keyspace) - } - if kschema == nil { - return nil, vterrors.Errorf(vtrpc.Code_UNAVAILABLE, "no VSchema for keyspace %v", msdw.keyspace) - } - - keyspaceSchema, err := vindexes.BuildKeyspaceSchema(kschema, msdw.keyspace) - if err != nil { - return nil, vterrors.Wrapf(err, "cannot build vschema for keyspace %v", msdw.keyspace) - } - return keyspaceSchema, nil -} - -// diff phase: will log messages regarding the diff. -// - get the schema on all tablets -// - if some table schema mismatches, record them (use existing schema diff tools). -// - for each table in destination, run a diff pipeline. - -func (msdw *MultiSplitDiffWorker) diff(ctx context.Context) error { - msdw.SetState(WorkerStateDiff) - - destinationSchemaDefinitions, sourceSchemaDefinition, err := msdw.gatherSchemaInfo(ctx) - if err != nil { - return err - } - err = msdw.diffSchemaInformation(ctx, destinationSchemaDefinitions, sourceSchemaDefinition) - if err != nil { - return err - } - - // read the vschema if needed - var keyspaceSchema *vindexes.KeyspaceSchema - keyspaceSchema, err = msdw.loadVSchema(ctx) - if err != nil { - return err - } - - msdw.wr.Logger().Infof("running the diffs...") - tableDefinitions := sourceSchemaDefinition.TableDefinitions - rec := &concurrency.AllErrorRecorder{} - - // sort tables by size - // if there are large deltas between table sizes then it's more efficient to start working on the large tables first - sort.Slice(tableDefinitions, func(i, j int) bool { return tableDefinitions[i].DataLength > tableDefinitions[j].DataLength }) - tableChan := make(chan *tabletmanagerdatapb.TableDefinition, len(tableDefinitions)) - for _, tableDefinition := range tableDefinitions { - tableChan <- tableDefinition - } - close(tableChan) - - consumers := sync.WaitGroup{} - // start as many goroutines we want parallel diffs running - for i := 0; i < msdw.parallelDiffsCount; i++ { - scanners := msdw.scanners[i] - if scanners.sourceScanner == nil || scanners.destinationScanner == nil { - return vterrors.Errorf(vtrpc.Code_INTERNAL, "should not be nil [%v] OR [%v]", scanners.sourceScanner, scanners.destinationScanner) - } - - consumers.Add(1) - go msdw.tableDiffingConsumer(ctx, &consumers, tableChan, rec, keyspaceSchema, scanners.sourceScanner, scanners.destinationScanner) - } - - // wait for all consumers to wrap up their work - consumers.Wait() - - return rec.Error() -} - -// markAsWillFail records the error and changes the state of the worker to reflect this -func (msdw *MultiSplitDiffWorker) markAsWillFail(er concurrency.ErrorRecorder, err error) { - er.RecordError(err) - msdw.SetState(WorkerStateDiffWillFail) -} diff --git a/go/vt/worker/multi_split_diff_cmd.go b/go/vt/worker/multi_split_diff_cmd.go deleted file mode 100644 index 8f8a6a5e549..00000000000 --- a/go/vt/worker/multi_split_diff_cmd.go +++ /dev/null @@ -1,260 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package worker - -import ( - "flag" - "fmt" - "html/template" - "net/http" - "strconv" - "strings" - "sync" - - "vitess.io/vitess/go/vt/proto/topodata" - "vitess.io/vitess/go/vt/proto/vtrpc" - "vitess.io/vitess/go/vt/vterrors" - - "context" - - "vitess.io/vitess/go/vt/concurrency" - "vitess.io/vitess/go/vt/topo/topoproto" - "vitess.io/vitess/go/vt/wrangler" -) - -const multiSplitDiffHTML = ` - - - Multi Split Diff Action - - -

    Multi Split Diff Action

    - - {{if .Error}} - Error: {{.Error}}
    - {{else}} - {{range $i, $si := .Shards}} -
  • {{$si.Keyspace}}/{{$si.Shard}}
  • - {{end}} - {{end}} - -` - -const multiSplitDiffHTML2 = ` - - - Multi Split Diff Action - - -

    Shard involved: {{.Keyspace}}/{{.Shard}}

    -

    Multi Split Diff Action

    -
    - -
    - - -
    - -
    - -
    - -
    - -
    - - ?
    - - - -
    - -` - -var multiSplitDiffTemplate = mustParseTemplate("multiSplitDiff", multiSplitDiffHTML) -var multiSplitDiffTemplate2 = mustParseTemplate("multiSplitDiff2", multiSplitDiffHTML2) - -func commandMultiSplitDiff(wi *Instance, wr *wrangler.Wrangler, subFlags *flag.FlagSet, args []string) (Worker, error) { - tabletTypeStr := subFlags.String("tablet_type", "RDONLY", "type of tablet used") - excludeTables := subFlags.String("exclude_tables", "", "comma separated list of tables to exclude") - minHealthyTablets := subFlags.Int("min_healthy_tablets", defaultMinHealthyTablets, "minimum number of healthy tablets before taking out one") - parallelDiffsCount := subFlags.Int("parallel_diffs_count", defaultParallelDiffsCount, "number of tables to diff in parallel") - waitForFixedTimeRatherThanGtidSet := subFlags.Bool("wait_for_fixed_time_rather_than_gtid_set", false, "wait for 1m when syncing up the destination RDONLY tablet rather than using the GTID set. Use this when the GTID set on the RDONLY is broken. Make sure the RDONLY is not behind in replication when using this flag.") - useConsistentSnapshot := subFlags.Bool("use_consistent_snapshot", defaultUseConsistentSnapshot, "Instead of pausing replication on the source, uses transactions with consistent snapshot to have a stable view of the data.") - if err := subFlags.Parse(args); err != nil { - return nil, err - } - if subFlags.NArg() != 1 { - subFlags.Usage() - return nil, vterrors.New(vtrpc.Code_INVALID_ARGUMENT, "command MultiSplitDiff requires ") - } - keyspace, shard, err := topoproto.ParseKeyspaceShard(subFlags.Arg(0)) - if err != nil { - return nil, err - } - var excludeTableArray []string - if *excludeTables != "" { - excludeTableArray = strings.Split(*excludeTables, ",") - } - - tabletType, ok := topodata.TabletType_value[*tabletTypeStr] - if !ok { - return nil, vterrors.Errorf(vtrpc.Code_INVALID_ARGUMENT, "failed to find this tablet type %v", tabletTypeStr) - } - - return NewMultiSplitDiffWorker(wr, wi.cell, keyspace, shard, excludeTableArray, *minHealthyTablets, *parallelDiffsCount, *waitForFixedTimeRatherThanGtidSet, *useConsistentSnapshot, topodata.TabletType(tabletType)), nil -} - -// shardSources returns all the shards that are SourceShards of at least one other shard. -func shardSources(ctx context.Context, wr *wrangler.Wrangler) ([]map[string]string, error) { - shortCtx, cancel := context.WithTimeout(ctx, *remoteActionsTimeout) - keyspaces, err := wr.TopoServer().GetKeyspaces(shortCtx) - cancel() - if err != nil { - return nil, vterrors.Wrap(err, "failed to get list of keyspaces") - } - - wg := sync.WaitGroup{} - mu := sync.Mutex{} // protects sourceShards - // Use a map to dedupe source shards - sourceShards := make(map[string]map[string]string) - rec := concurrency.AllErrorRecorder{} - for _, keyspace := range keyspaces { - wg.Add(1) - go func(keyspace string) { - defer wg.Done() - shortCtx, cancel := context.WithTimeout(ctx, *remoteActionsTimeout) - shards, err := wr.TopoServer().GetShardNames(shortCtx, keyspace) - cancel() - if err != nil { - rec.RecordError(vterrors.Wrapf(err, "failed to get list of shards for keyspace '%v'", keyspace)) - return - } - for _, shard := range shards { - wg.Add(1) - go func(keyspace, shard string) { - defer wg.Done() - shortCtx, cancel := context.WithTimeout(ctx, *remoteActionsTimeout) - si, err := wr.TopoServer().GetShard(shortCtx, keyspace, shard) - cancel() - if err != nil { - rec.RecordError(vterrors.Wrapf(err, "failed to get details for shard '%v'", topoproto.KeyspaceShardString(keyspace, shard))) - return - } - - if len(si.SourceShards) > 0 && len(si.SourceShards[0].Tables) == 0 { - mu.Lock() - for _, sourceShard := range si.SourceShards { - sourceShards[fmt.Sprintf("%v/%v", sourceShard.Keyspace, sourceShard.Shard)] = - map[string]string{ - "Keyspace": sourceShard.Keyspace, - "Shard": sourceShard.Shard, - } - } - mu.Unlock() - } - }(keyspace, shard) - } - }(keyspace) - } - wg.Wait() - - if rec.HasErrors() { - return nil, rec.Error() - } - result := make([]map[string]string, 0, len(sourceShards)) - for _, shard := range sourceShards { - result = append(result, shard) - } - if len(result) == 0 { - return nil, vterrors.Errorf(vtrpc.Code_FAILED_PRECONDITION, "there are no shards with SourceShards") - } - return result, nil -} - -func interactiveMultiSplitDiff(ctx context.Context, wi *Instance, wr *wrangler.Wrangler, _ http.ResponseWriter, r *http.Request) (Worker, *template.Template, map[string]any, error) { - if err := r.ParseForm(); err != nil { - return nil, nil, nil, vterrors.Wrap(err, "cannot parse form") - } - keyspace := r.FormValue("keyspace") - shard := r.FormValue("shard") - - if keyspace == "" || shard == "" { - // display the list of possible shards to chose from - result := make(map[string]any) - shards, err := shardSources(ctx, wr) - if err != nil { - result["Error"] = err.Error() - } else { - result["Shards"] = shards - } - return nil, multiSplitDiffTemplate, result, nil - } - - submitButtonValue := r.FormValue("submit") - if submitButtonValue == "" { - // display the input form - result := make(map[string]any) - result["Keyspace"] = keyspace - result["Shard"] = shard - result["DefaultSourceUID"] = "0" - result["DefaultMinHealthyTablets"] = fmt.Sprintf("%v", defaultMinHealthyTablets) - result["DefaultParallelDiffsCount"] = fmt.Sprintf("%v", defaultParallelDiffsCount) - return nil, multiSplitDiffTemplate2, result, nil - } - - // Process input form. - excludeTables := r.FormValue("excludeTables") - var excludeTableArray []string - if excludeTables != "" { - excludeTableArray = strings.Split(excludeTables, ",") - } - minHealthyTabletsStr := r.FormValue("minHealthyTablets") - parallelDiffsCountStr := r.FormValue("parallelDiffsCount") - minHealthyTablets, err := strconv.ParseInt(minHealthyTabletsStr, 0, 64) - if err != nil { - return nil, nil, nil, vterrors.Wrap(err, "cannot parse minHealthyTablets") - } - parallelDiffsCount, err := strconv.ParseInt(parallelDiffsCountStr, 0, 64) - if err != nil { - return nil, nil, nil, fmt.Errorf("cannot parse parallelDiffsCount: %s", err) - } - waitForFixedTimeRatherThanGtidSetStr := r.FormValue("waitForFixedTimeRatherThanGtidSet") - waitForFixedTimeRatherThanGtidSet := waitForFixedTimeRatherThanGtidSetStr == "true" - useConsistentSnapshotStr := r.FormValue("useConsistentSnapshot") - useConsistentSnapshot := useConsistentSnapshotStr == "true" - - tabletTypeStr := r.FormValue("tabletType") - tabletType, ok := topodata.TabletType_value[tabletTypeStr] - if !ok { - return nil, nil, nil, vterrors.Errorf(vtrpc.Code_INVALID_ARGUMENT, "cannot parse tabletType: %s", tabletTypeStr) - } - - // start the diff job - wrk := NewMultiSplitDiffWorker(wr, wi.cell, keyspace, shard, excludeTableArray, int(minHealthyTablets), int(parallelDiffsCount), waitForFixedTimeRatherThanGtidSet, useConsistentSnapshot, topodata.TabletType(tabletType)) - return wrk, nil, nil, nil -} - -func init() { - AddCommand("Diffs", Command{"MultiSplitDiff", - commandMultiSplitDiff, interactiveMultiSplitDiff, - "[--exclude_tables=''] ", - "Diffs a rdonly destination shard against its SourceShards"}) -} diff --git a/go/vt/worker/multi_split_diff_test.go b/go/vt/worker/multi_split_diff_test.go deleted file mode 100644 index 95f1e0bdd7d..00000000000 --- a/go/vt/worker/multi_split_diff_test.go +++ /dev/null @@ -1,326 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package worker - -import ( - "context" - "fmt" - "strings" - "testing" - "time" - - "vitess.io/vitess/go/sqltypes" - "vitess.io/vitess/go/vt/discovery" - "vitess.io/vitess/go/vt/logutil" - "vitess.io/vitess/go/vt/mysqlctl/tmutils" - "vitess.io/vitess/go/vt/topo/memorytopo" - "vitess.io/vitess/go/vt/topotools" - "vitess.io/vitess/go/vt/vttablet/grpcqueryservice" - "vitess.io/vitess/go/vt/vttablet/queryservice/fakes" - "vitess.io/vitess/go/vt/wrangler" - "vitess.io/vitess/go/vt/wrangler/testlib" - - querypb "vitess.io/vitess/go/vt/proto/query" - tabletmanagerdatapb "vitess.io/vitess/go/vt/proto/tabletmanagerdata" - topodatapb "vitess.io/vitess/go/vt/proto/topodata" - vschemapb "vitess.io/vitess/go/vt/proto/vschema" -) - -// msdDestinationTabletServer is a local QueryService implementation to -// support the tests -type msdDestinationTabletServer struct { - t *testing.T - - *fakes.StreamHealthQueryService - excludedTable string - shardIndex int -} - -func (sq *msdDestinationTabletServer) StreamExecute(ctx context.Context, target *querypb.Target, sql string, bindVariables map[string]*querypb.BindVariable, transactionID int64, reservedID int64, options *querypb.ExecuteOptions, callback func(*sqltypes.Result) error) error { - if strings.Contains(sql, sq.excludedTable) { - sq.t.Errorf("Split Diff operation on destination should skip the excluded table: %v query: %v", sq.excludedTable, sql) - } - - if hasKeyspace := strings.Contains(sql, "WHERE `keyspace_id`"); hasKeyspace == true { - sq.t.Errorf("Sql query on destination should not contain a keyspace_id WHERE clause; query received: %v", sql) - } - - sq.t.Logf("msdDestinationTabletServer: got query: %v", sql) - - // Send the headers - if err := callback(&sqltypes.Result{ - Fields: []*querypb.Field{ - { - Name: "id", - Type: sqltypes.Int64, - }, - { - Name: "msg", - Type: sqltypes.VarChar, - }, - { - Name: "keyspace_id", - Type: sqltypes.Int64, - }, - }, - }); err != nil { - return err - } - - // Send the values - ksids := []uint64{0x2000000000000000, 0x6000000000000000} - for i := 0; i < 100; i++ { - // skip the out-of-range values - if i%2 == sq.shardIndex { - continue - } - if err := callback(&sqltypes.Result{ - Rows: [][]sqltypes.Value{ - { - sqltypes.NewVarBinary(fmt.Sprintf("%v", i)), - sqltypes.NewVarBinary(fmt.Sprintf("Text for %v", i)), - sqltypes.NewVarBinary(fmt.Sprintf("%v", ksids[i%2])), - }, - }, - }); err != nil { - return err - } - } - return nil -} - -// msdSourceTabletServer is a local QueryService implementation to support the tests -type msdSourceTabletServer struct { - t *testing.T - - *fakes.StreamHealthQueryService - excludedTable string - v3 bool -} - -func (sq *msdSourceTabletServer) StreamExecute(ctx context.Context, target *querypb.Target, sql string, bindVariables map[string]*querypb.BindVariable, transactionID int64, reservedID int64, options *querypb.ExecuteOptions, callback func(*sqltypes.Result) error) error { - if strings.Contains(sql, sq.excludedTable) { - sq.t.Errorf("Split Diff operation on source should skip the excluded table: %v query: %v", sq.excludedTable, sql) - } - - // we test for a keyspace_id where clause, except for v3 - if !sq.v3 { - if hasKeyspace := strings.Contains(sql, "WHERE `keyspace_id` < 4611686018427387904"); hasKeyspace != true { - sq.t.Errorf("Sql query on source should contain a keyspace_id WHERE clause; query received: %v", sql) - } - } - - sq.t.Logf("msdSourceTabletServer: got query: %v", sql) - - // Send the headers - if err := callback(&sqltypes.Result{ - Fields: []*querypb.Field{ - { - Name: "id", - Type: sqltypes.Int64, - }, - { - Name: "msg", - Type: sqltypes.VarChar, - }, - { - Name: "keyspace_id", - Type: sqltypes.Int64, - }, - }, - }); err != nil { - return err - } - - // Send the values - ksids := []uint64{0x2000000000000000, 0x6000000000000000} - for i := 0; i < 100; i++ { - if !sq.v3 && i%2 == 1 { - // for v2, filtering is done at SQL layer - continue - } - if err := callback(&sqltypes.Result{ - Rows: [][]sqltypes.Value{ - { - sqltypes.NewVarBinary(fmt.Sprintf("%v", i)), - sqltypes.NewVarBinary(fmt.Sprintf("Text for %v", i)), - sqltypes.NewVarBinary(fmt.Sprintf("%v", ksids[i%2])), - }, - }, - }); err != nil { - return err - } - } - return nil -} - -// TODO(aaijazi): Create a test in which source and destination data does not match - -func testMultiSplitDiff(t *testing.T) { - ts := memorytopo.NewServer("cell1", "cell2") - ctx := context.Background() - wi := NewInstance(ts, "cell1", time.Second) - - if err := ts.CreateKeyspace(ctx, "ks", &topodatapb.Keyspace{}); err != nil { - t.Fatalf("CreateKeyspace v3 failed: %v", err) - } - - vs := &vschemapb.Keyspace{ - Sharded: true, - Vindexes: map[string]*vschemapb.Vindex{ - "table1_index": { - Type: "numeric", - }, - }, - Tables: map[string]*vschemapb.Table{ - "table1": { - ColumnVindexes: []*vschemapb.ColumnVindex{ - { - Column: "keyspace_id", - Name: "table1_index", - }, - }, - }, - }, - } - if err := ts.SaveVSchema(ctx, "ks", vs); err != nil { - t.Fatalf("SaveVSchema v3 failed: %v", err) - } - - sourcePrimary := testlib.NewFakeTablet(t, wi.wr, "cell1", 0, - topodatapb.TabletType_PRIMARY, nil, testlib.TabletKeyspaceShard(t, "ks", "-80")) - sourceRdonly1 := testlib.NewFakeTablet(t, wi.wr, "cell1", 1, - topodatapb.TabletType_RDONLY, nil, testlib.TabletKeyspaceShard(t, "ks", "-80")) - sourceRdonly2 := testlib.NewFakeTablet(t, wi.wr, "cell1", 2, - topodatapb.TabletType_RDONLY, nil, testlib.TabletKeyspaceShard(t, "ks", "-80")) - - leftPrimary := testlib.NewFakeTablet(t, wi.wr, "cell1", 10, - topodatapb.TabletType_PRIMARY, nil, testlib.TabletKeyspaceShard(t, "ks", "-40")) - leftRdonly1 := testlib.NewFakeTablet(t, wi.wr, "cell1", 11, - topodatapb.TabletType_RDONLY, nil, testlib.TabletKeyspaceShard(t, "ks", "-40")) - leftRdonly2 := testlib.NewFakeTablet(t, wi.wr, "cell1", 12, - topodatapb.TabletType_RDONLY, nil, testlib.TabletKeyspaceShard(t, "ks", "-40")) - - rightPrimary := testlib.NewFakeTablet(t, wi.wr, "cell1", 20, - topodatapb.TabletType_PRIMARY, nil, testlib.TabletKeyspaceShard(t, "ks", "40-80")) - rightRdonly1 := testlib.NewFakeTablet(t, wi.wr, "cell1", 21, - topodatapb.TabletType_RDONLY, nil, testlib.TabletKeyspaceShard(t, "ks", "40-80")) - rightRdonly2 := testlib.NewFakeTablet(t, wi.wr, "cell1", 22, - topodatapb.TabletType_RDONLY, nil, testlib.TabletKeyspaceShard(t, "ks", "40-80")) - - // add the topo and schema data we'll need - if err := ts.CreateShard(ctx, "ks", "80-"); err != nil { - t.Fatalf("CreateShard(\"-80\") failed: %v", err) - } - wi.wr.SetSourceShards(ctx, "ks", "-40", []*topodatapb.TabletAlias{sourceRdonly1.Tablet.Alias}, nil) - wi.wr.SetSourceShards(ctx, "ks", "40-80", []*topodatapb.TabletAlias{sourceRdonly1.Tablet.Alias}, nil) - if err := topotools.RebuildKeyspace(ctx, wi.wr.Logger(), wi.wr.TopoServer(), "ks", nil, false); err != nil { - t.Fatalf("RebuildKeyspaceGraph failed: %v", err) - } - - excludedTable := "excludedTable1" - - for _, rdonly := range []*testlib.FakeTablet{sourceRdonly1, sourceRdonly2, leftRdonly1, leftRdonly2, rightRdonly1, rightRdonly2} { - // The destination only has half the data. - // For v2, we do filtering at the SQL level. - // For v3, we do it in the client. - // So in any case, we need real data. - rdonly.FakeMysqlDaemon.Schema = &tabletmanagerdatapb.SchemaDefinition{ - DatabaseSchema: "", - TableDefinitions: []*tabletmanagerdatapb.TableDefinition{ - { - Name: "table1", - Columns: []string{"id", "msg", "keyspace_id"}, - PrimaryKeyColumns: []string{"id"}, - Type: tmutils.TableBaseTable, - }, - { - Name: excludedTable, - Columns: []string{"id", "msg", "keyspace_id"}, - PrimaryKeyColumns: []string{"id"}, - Type: tmutils.TableBaseTable, - }, - }, - } - } - - for _, sourceRdonly := range []*testlib.FakeTablet{sourceRdonly1, sourceRdonly2} { - qs := fakes.NewStreamHealthQueryService(sourceRdonly.Target()) - qs.AddDefaultHealthResponse() - grpcqueryservice.Register(sourceRdonly.RPCServer, &msdSourceTabletServer{ - t: t, - - StreamHealthQueryService: qs, - excludedTable: excludedTable, - v3: true, - }) - } - - for _, destRdonly := range []*testlib.FakeTablet{leftRdonly1, leftRdonly2} { - qs := fakes.NewStreamHealthQueryService(destRdonly.Target()) - qs.AddDefaultHealthResponse() - grpcqueryservice.Register(destRdonly.RPCServer, &msdDestinationTabletServer{ - t: t, - - StreamHealthQueryService: qs, - excludedTable: excludedTable, - shardIndex: 0, - }) - } - - for _, destRdonly := range []*testlib.FakeTablet{rightRdonly1, rightRdonly2} { - qs := fakes.NewStreamHealthQueryService(destRdonly.Target()) - qs.AddDefaultHealthResponse() - grpcqueryservice.Register(destRdonly.RPCServer, &msdDestinationTabletServer{ - t: t, - - StreamHealthQueryService: qs, - excludedTable: excludedTable, - shardIndex: 1, - }) - } - - // Start action loop after having registered all RPC services. - for _, ft := range []*testlib.FakeTablet{sourcePrimary, sourceRdonly1, sourceRdonly2, leftPrimary, leftRdonly1, leftRdonly2, rightPrimary, rightRdonly1, rightRdonly2} { - ft.StartActionLoop(t, wi.wr) - defer ft.StopActionLoop(t) - } - - // Run the vtworker command. - args := []string{ - "MultiSplitDiff", - "-exclude_tables", excludedTable, - "ks/-80", - } - // We need to use FakeTabletManagerClient because we don't - // have a good way to fake the binlog player yet, which is - // necessary for synchronizing replication. - wr := wrangler.New(logutil.NewConsoleLogger(), ts, newFakeTMCTopo(ts)) - if err := runCommand(t, wi, wr, args); err != nil { - t.Fatalf("%+v", err) - } -} - -func TestMultiSplitDiffv3(t *testing.T) { - delay := discovery.GetTabletPickerRetryDelay() - defer func() { - discovery.SetTabletPickerRetryDelay(delay) - }() - discovery.SetTabletPickerRetryDelay(5 * time.Millisecond) - - testMultiSplitDiff(t) -} diff --git a/go/vt/worker/panic.go b/go/vt/worker/panic.go deleted file mode 100644 index 05e7602b0f1..00000000000 --- a/go/vt/worker/panic.go +++ /dev/null @@ -1,87 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package worker - -import ( - "html/template" - - "context" - - "vitess.io/vitess/go/vt/wrangler" -) - -// PanicWorker will run panic() when executed. For internal tests only. -type PanicWorker struct { - StatusWorker - - // We use the Wrangler's logger to print the message. - wr *wrangler.Wrangler -} - -// NewPanicWorker returns a new PanicWorker object. -func NewPanicWorker(wr *wrangler.Wrangler) (Worker, error) { - return &PanicWorker{ - StatusWorker: NewStatusWorker(), - wr: wr, - }, nil -} - -// StatusAsHTML implements the Worker interface -func (pw *PanicWorker) StatusAsHTML() template.HTML { - state := pw.State() - - result := "Panic Command
    \n" - result += "State: " + state.String() + "
    \n" - switch state { - case WorkerStateDone: - result += "Success:
    \n" - result += "panic() should have been executed and logged by the vtworker framework.
    \n" - } - - return template.HTML(result) -} - -// StatusAsText implements the Worker interface. -func (pw *PanicWorker) StatusAsText() string { - state := pw.State() - - result := "Panic Command\n" - result += "State: " + state.String() + "\n" - switch state { - case WorkerStateDone: - result += "panic() should have been executed and logged by the vtworker framework.\n" - } - return result -} - -// Run implements the Worker interface. -func (pw *PanicWorker) Run(ctx context.Context) error { - resetVars() - err := pw.run(ctx) - - pw.SetState(WorkerStateCleanUp) - if err != nil { - pw.SetState(WorkerStateError) - return err - } - pw.SetState(WorkerStateDone) - return nil -} - -func (pw *PanicWorker) run(ctx context.Context) error { - panic("Panic command was called. This should be caught by the vtworker framework and logged as an error.") -} diff --git a/go/vt/worker/panic_cmd.go b/go/vt/worker/panic_cmd.go deleted file mode 100644 index 5f4cd91518f..00000000000 --- a/go/vt/worker/panic_cmd.go +++ /dev/null @@ -1,52 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package worker - -import ( - "flag" - "html/template" - "net/http" - - "vitess.io/vitess/go/vt/vterrors" - - "context" - - "vitess.io/vitess/go/vt/wrangler" -) - -func commandPanic(wi *Instance, wr *wrangler.Wrangler, subFlags *flag.FlagSet, args []string) (Worker, error) { - worker, err := NewPanicWorker(wr) - if err != nil { - return nil, vterrors.Wrap(err, "Could not create Panic worker") - } - return worker, nil -} - -func interactivePanic(ctx context.Context, wi *Instance, wr *wrangler.Wrangler, w http.ResponseWriter, r *http.Request) (Worker, *template.Template, map[string]any, error) { - wrk, err := NewPanicWorker(wr) - if err != nil { - return nil, nil, nil, vterrors.Wrap(err, "Could not create Panic worker") - } - return wrk, nil, nil, nil -} - -func init() { - AddCommand("Debugging", Command{"Panic", - commandPanic, interactivePanic, - "", - "For internal tests only. Will call panic() when executed."}) -} diff --git a/go/vt/worker/ping.go b/go/vt/worker/ping.go deleted file mode 100644 index fb004910c1e..00000000000 --- a/go/vt/worker/ping.go +++ /dev/null @@ -1,99 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package worker - -import ( - "html/template" - - "context" - - "vitess.io/vitess/go/vt/wrangler" -) - -// PingWorker will log a message with level CONSOLE. -type PingWorker struct { - StatusWorker - - // We use the Wrangler's logger to print the message. - wr *wrangler.Wrangler - message string -} - -// NewPingWorker returns a new PingWorker object. -func NewPingWorker(wr *wrangler.Wrangler, message string) (Worker, error) { - return &PingWorker{ - StatusWorker: NewStatusWorker(), - wr: wr, - message: message, - }, nil -} - -// StatusAsHTML implements the Worker interface -func (pw *PingWorker) StatusAsHTML() template.HTML { - state := pw.State() - - result := "Ping Command with message: '" + pw.message + "'
    \n" - result += "State: " + state.String() + "
    \n" - switch state { - case WorkerStateDebugRunning: - result += "Running:
    \n" - result += "Logging message: '" + pw.message + "'
    \n" - case WorkerStateDone: - result += "Success:
    \n" - result += "Logged message: '" + pw.message + "'
    \n" - } - - return template.HTML(result) -} - -// StatusAsText implements the Worker interface. -func (pw *PingWorker) StatusAsText() string { - state := pw.State() - - result := "Ping Command with message: '" + pw.message + "'\n" - result += "State: " + state.String() + "\n" - switch state { - case WorkerStateDebugRunning: - result += "Logging message: '" + pw.message + "'\n" - case WorkerStateDone: - result += "Logged message: '" + pw.message + "'\n" - } - return result -} - -// Run implements the Worker interface. -func (pw *PingWorker) Run(ctx context.Context) error { - resetVars() - err := pw.run(ctx) - - pw.SetState(WorkerStateCleanUp) - if err != nil { - pw.SetState(WorkerStateError) - return err - } - pw.SetState(WorkerStateDone) - return nil -} - -func (pw *PingWorker) run(ctx context.Context) error { - // We reuse the Copy state to reflect that the logging is in progress. - pw.SetState(WorkerStateDebugRunning) - pw.wr.Logger().Printf("Ping command was called with message: '%v'.\n", pw.message) - pw.SetState(WorkerStateDone) - - return nil -} diff --git a/go/vt/worker/ping_cmd.go b/go/vt/worker/ping_cmd.go deleted file mode 100644 index bcdea2db49a..00000000000 --- a/go/vt/worker/ping_cmd.go +++ /dev/null @@ -1,94 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package worker - -import ( - "flag" - "html/template" - "net/http" - - "vitess.io/vitess/go/vt/proto/vtrpc" - "vitess.io/vitess/go/vt/vterrors" - - "context" - - "vitess.io/vitess/go/vt/wrangler" -) - -const pingHTML = ` - - - Ping Action - - -

    Ping Action

    - - {{if .Error}} - Error: {{.Error}}
    - {{else}} -
    - -
    - -
    - {{end}} - -` - -var pingTemplate = mustParseTemplate("ping", pingHTML) - -func commandPing(wi *Instance, wr *wrangler.Wrangler, subFlags *flag.FlagSet, args []string) (Worker, error) { - if err := subFlags.Parse(args); err != nil { - return nil, err - } - if subFlags.NArg() != 1 { - subFlags.Usage() - return nil, vterrors.Errorf(vtrpc.Code_INVALID_ARGUMENT, "command Ping requires ") - } - message := subFlags.Arg(0) - - worker, err := NewPingWorker(wr, message) - if err != nil { - return nil, vterrors.Wrap(err, "Could not create Ping worker") - } - return worker, nil -} - -func interactivePing(ctx context.Context, wi *Instance, wr *wrangler.Wrangler, w http.ResponseWriter, r *http.Request) (Worker, *template.Template, map[string]any, error) { - if err := r.ParseForm(); err != nil { - return nil, nil, nil, vterrors.Wrap(err, "Cannot parse form") - } - - message := r.FormValue("message") - if message == "" { - result := make(map[string]any) - return nil, pingTemplate, result, nil - } - - wrk, err := NewPingWorker(wr, message) - if err != nil { - return nil, nil, nil, vterrors.Wrap(err, "Could not create Ping worker") - } - return wrk, nil, nil, nil -} - -func init() { - AddCommand("Debugging", Command{"Ping", - commandPing, interactivePing, - "", - "For internal tests only. will be logged as CONSOLE output."}) -} diff --git a/go/vt/worker/restartable_result_reader.go b/go/vt/worker/restartable_result_reader.go deleted file mode 100644 index 620e54bda5f..00000000000 --- a/go/vt/worker/restartable_result_reader.go +++ /dev/null @@ -1,430 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package worker - -import ( - "io" - "strings" - "time" - - "vitess.io/vitess/go/vt/vterrors" - - "context" - - "vitess.io/vitess/go/sqlescape" - "vitess.io/vitess/go/sqltypes" - "vitess.io/vitess/go/vt/grpcclient" - "vitess.io/vitess/go/vt/log" - "vitess.io/vitess/go/vt/logutil" - "vitess.io/vitess/go/vt/topo/topoproto" - "vitess.io/vitess/go/vt/vttablet/queryservice" - "vitess.io/vitess/go/vt/vttablet/tabletconn" - - querypb "vitess.io/vitess/go/vt/proto/query" - tabletmanagerdatapb "vitess.io/vitess/go/vt/proto/tabletmanagerdata" - topodatapb "vitess.io/vitess/go/vt/proto/topodata" -) - -// RestartableResultReader will stream all rows within a chunk. -// If the streaming query gets interrupted, it can resume the stream after -// the last row which was read. -type RestartableResultReader struct { - ctx context.Context - logger logutil.Logger - tp tabletProvider - // td is used to get the list of primary key columns at a restart. - td *tabletmanagerdatapb.TableDefinition - chunk chunk - // allowMultipleRetries is true if we are allowed to retry more than once. - allowMultipleRetries bool - // if we are running inside a transaction, this will hold a non-zero value - txID int64 - - query string - - tablet *topodatapb.Tablet - conn queryservice.QueryService - fields []*querypb.Field - output sqltypes.ResultStream - - // lastRow is used during a restart to determine after which row the restart - // should start. - lastRow []sqltypes.Value -} - -// NewRestartableResultReader creates a new RestartableResultReader for -// the provided tablet and chunk. -// It will automatically create the necessary query to read all rows within -// the chunk. -// NOTE: We assume that the Columns field in "td" was ordered by a preceding -// call to reorderColumnsPrimaryKeyFirst(). -func NewRestartableResultReader(ctx context.Context, logger logutil.Logger, tp tabletProvider, td *tabletmanagerdatapb.TableDefinition, chunk chunk, allowMultipleRetries bool) (*RestartableResultReader, error) { - r := &RestartableResultReader{ - ctx: ctx, - logger: logger, - tp: tp, - td: td, - chunk: chunk, - allowMultipleRetries: allowMultipleRetries, - } - - err := tryToConnect(r) - if err != nil { - return nil, err - } - return r, nil -} - -func tryToConnect(r *RestartableResultReader) error { - - // If the initial connection fails we retry once. - // Note: The first retry will be the second attempt. - attempt := 0 - for { - attempt++ - var err error - var retryable bool - if retryable, err = r.getTablet(); err != nil { - err = vterrors.Wrap(err, "tablet=unknown") - goto retry - } - if retryable, err = r.startStream(); err != nil { - err = vterrors.Wrapf(err, "tablet=%v", topoproto.TabletAliasString(r.tablet.Alias)) - goto retry - } - return nil - - retry: - if !retryable || attempt > 1 { - return vterrors.Wrapf(err, "failed to initialize tablet connection: retryable %v", retryable) - } - statsRetryCount.Add(1) - log.Infof("retrying after error: %v", err) - } - -} - -// NewTransactionalRestartableResultReader does the same thing that NewRestartableResultReader does, -// but works inside of a single transaction -func NewTransactionalRestartableResultReader(ctx context.Context, logger logutil.Logger, tp tabletProvider, td *tabletmanagerdatapb.TableDefinition, chunk chunk, allowMultipleRetries bool, txID int64) (*RestartableResultReader, error) { - r := &RestartableResultReader{ - ctx: ctx, - logger: logger, - tp: tp, - td: td, - chunk: chunk, - allowMultipleRetries: allowMultipleRetries, - txID: txID, - } - err := tryToConnect(r) - if err != nil { - return nil, err - } - return r, nil -} - -// getTablet (re)sets the tablet which is used for the streaming query. -// If the method returns an error, the first return value specifies if it is -// okay to retry. -func (r *RestartableResultReader) getTablet() (bool, error) { - if r.tablet != nil { - // If there was a tablet before, return it to the tabletProvider. - r.Close(r.ctx) - - r.tablet = nil - r.conn = nil - r.fields = nil - r.output = nil - } - - // Get a tablet from the tablet provider. - tablet, err := r.tp.getTablet() - if err != nil { - return true /* retryable */, vterrors.Wrap(err, "failed get tablet for streaming query") - } - - // Connect (dial) to the tablet. - conn, err := tabletconn.GetDialer()(tablet, grpcclient.FailFast(false)) - if err != nil { - return false /* retryable */, vterrors.Wrap(err, "failed to get dialer for tablet") - } - r.tablet = tablet - r.conn = conn - return false /* retryable */, nil -} - -// startStream assumes that getTablet() was successfully called before and now -// tries to connect to the set tablet and start the streaming query. -// If the method returns an error, the first return value specifies if it is -// okay to retry. -func (r *RestartableResultReader) startStream() (bool, error) { - // Start the streaming query. - r.generateQuery() - var stream sqltypes.ResultStream - - if r.txID == 0 { - stream = queryservice.ExecuteWithStreamer(r.ctx, r.conn, &querypb.Target{ - Keyspace: r.tablet.Keyspace, - Shard: r.tablet.Shard, - TabletType: r.tablet.Type, - }, r.query, make(map[string]*querypb.BindVariable), nil) - } else { - stream = queryservice.ExecuteWithTransactionalStreamer(r.ctx, r.conn, &querypb.Target{ - Keyspace: r.tablet.Keyspace, - Shard: r.tablet.Shard, - TabletType: r.tablet.Type, - }, r.query, make(map[string]*querypb.BindVariable), r.txID, nil) - } - - // Read the fields information. - cols, err := stream.Recv() - if err != nil { - return true /* retryable */, vterrors.Wrapf(err, "cannot read Fields for query '%v'", r.query) - } - r.fields = cols.Fields - r.output = stream - - alias := topoproto.TabletAliasString(r.tablet.Alias) - statsStreamingQueryCounters.Add(alias, 1) - log.V(2).Infof("tablet=%v table=%v chunk=%v: Starting to stream rows using query '%v'.", alias, r.td.Name, r.chunk, r.query) - return false, nil -} - -// Next returns the next result on the stream. It implements ResultReader. -func (r *RestartableResultReader) Next() (*sqltypes.Result, error) { - result, err := r.output.Recv() - if err != nil && err != io.EOF { - // We start the retries only on the second attempt to avoid the cost - // of starting a timer (for the retry timeout) for every Next() call - // when no error occurs. - alias := topoproto.TabletAliasString(r.tablet.Alias) - statsStreamingQueryErrorsCounters.Add(alias, 1) - log.V(2).Infof("tablet=%v table=%v chunk=%v: Failed to read next rows from active streaming query. Trying to restart stream on the same tablet. Original Error: %v", alias, r.td.Name, r.chunk, err) - result, err = r.nextWithRetries() - } - if result != nil && len(result.Rows) > 0 { - r.lastRow = result.Rows[len(result.Rows)-1] - } - return result, err -} - -func (r *RestartableResultReader) nextWithRetries() (*sqltypes.Result, error) { - // In case of errors we will keep retrying until retryCtx is done. - retryCtx, retryCancel := context.WithTimeout(r.ctx, *retryDuration) - defer retryCancel() - - // The first retry is the second attempt because we already tried once in Next() - attempt := 1 - start := time.Now() - for { - attempt++ - - var result *sqltypes.Result - var retryable bool - var err error - - if attempt > 2 { - // Do not failover to a different tablet at the 2nd attempt, but always - // after that. - // That's because the first restart is meant to fix transient problems - // which go away at the next retry. For example, when MySQL killed the - // vttablet connection due to net_write_timeout being reached. - retryable, err = r.getTablet() - if err != nil { - if !retryable { - r.logger.Errorf2(err, "table=%v chunk=%v: Failed to restart streaming query (attempt %d) and failover to a different tablet (%v) due to a non-retryable error", r.td.Name, r.chunk, attempt, r.tablet) - return nil, err - } - goto retry - } - } - - if attempt > 1 { - // Restart streaming query. - retryable, err = r.startStream() - if err != nil { - if !retryable { - r.logger.Errorf2(err, "tablet=%v table=%v chunk=%v: Failed to restart streaming query (attempt %d) with query '%v' and stopped due to a non-retryable error", topoproto.TabletAliasString(r.tablet.Alias), r.td.Name, r.chunk, attempt, r.query) - return nil, err - } - goto retry - } - } - - result, err = r.output.Recv() - if err == nil || err == io.EOF { - alias := topoproto.TabletAliasString(r.tablet.Alias) - log.V(2).Infof("tablet=%v table=%v chunk=%v: Successfully restarted streaming query with query '%v' after %.1f seconds.", alias, r.td.Name, r.chunk, r.query, time.Since(start).Seconds()) - if attempt == 2 { - statsStreamingQueryRestartsSameTabletCounters.Add(alias, 1) - } else { - statsStreamingQueryRestartsDifferentTablet.Add(1) - } - - // Recv() was successful. - return result, err - } - - retry: - if attempt == 2 && !r.allowMultipleRetries { - // Offline source tablets must not be retried forever. Fail early. - return nil, vterrors.Wrapf(err, "%v: first retry to restart the streaming query on the same tablet failed. We're failing at this point because we're not allowed to keep retrying. err", r.tp.description()) - } - - alias := "unknown" - if r.tablet != nil { - alias = topoproto.TabletAliasString(r.tablet.Alias) - // tablet may be nil if e.g. the HealthCheck module currently does not - // return a tablet. - statsStreamingQueryErrorsCounters.Add(alias, 1) - } - - deadline, _ := retryCtx.Deadline() - log.V(2).Infof("tablet=%v table=%v chunk=%v: Failed to restart streaming query (attempt %d) with query '%v'. Retrying to restart stream on a different tablet (for up to %.1f minutes). Next retry is in %.1f seconds. Error: %v", alias, r.td.Name, r.chunk, attempt, r.query, time.Until(deadline).Minutes(), executeFetchRetryTime.Seconds(), err) - - select { - case <-retryCtx.Done(): - err := retryCtx.Err() - if err == context.DeadlineExceeded { - return nil, vterrors.Wrapf(err, "%v: failed to restart the streaming connection after retrying for %v", r.tp.description(), *retryDuration) - } - return nil, vterrors.Wrapf(err, "%v: interrupted while trying to restart the streaming connection (%.1f minutes elapsed so far)", r.tp.description(), time.Since(start).Minutes()) - case <-time.After(*executeFetchRetryTime): - // Make a pause between the retries to avoid hammering the servers. - } - } -} - -// Fields returns the field data. It implements ResultReader. -func (r *RestartableResultReader) Fields() []*querypb.Field { - return r.fields -} - -// Close closes the connection to the tablet. -func (r *RestartableResultReader) Close(ctx context.Context) { - if r.conn != nil { - r.conn.Close(ctx) - r.tp.returnTablet(r.tablet) - } -} - -func (r *RestartableResultReader) generateQuery() { - query := "SELECT " + strings.Join(escapeAll(r.td.Columns), ",") + " FROM " + sqlescape.EscapeID(r.td.Name) - - // Build WHERE clauses. - var clauses []string - - // start value. - if r.lastRow == nil { - // Initial query. - if !r.chunk.start.IsNull() { - var b strings.Builder - sqlescape.WriteEscapeID(&b, r.td.PrimaryKeyColumns[0]) - b.WriteString(">=") - r.chunk.start.EncodeSQL(&b) - clauses = append(clauses, b.String()) - } - } else { - // This is a restart. Read after the last row. - // Note that we don't have to be concerned that the new start might be > end - // because lastRow < end is always true. That's because the initial query - // had the clause 'WHERE PrimaryKeyColumns[0] < end'. - // TODO(mberlin): Write an e2e test to verify that restarts also work with - // string types and MySQL collation rules. - clauses = append(clauses, greaterThanTupleWhereClause(r.td.PrimaryKeyColumns, r.lastRow)...) - } - - // end value. - if !r.chunk.end.IsNull() { - var b strings.Builder - sqlescape.WriteEscapeID(&b, r.td.PrimaryKeyColumns[0]) - b.WriteString("<") - r.chunk.end.EncodeSQL(&b) - clauses = append(clauses, b.String()) - } - - if len(clauses) > 0 { - query += " WHERE " + strings.Join(clauses, " AND ") - } - if len(r.td.PrimaryKeyColumns) > 0 { - query += " ORDER BY " + strings.Join(escapeAll(r.td.PrimaryKeyColumns), ",") - } - r.query = query -} - -// greaterThanTupleWhereClause builds a greater than (">") WHERE clause -// expression for the first "columns" in "row". -// The caller has to ensure that "columns" matches with the values in "row". -// Examples: -// one column: a > 1 -// two columns: a>=1 AND (a,b) > (1,2) -// (Input for that would be: columns{"a", "b"}, row{1, 2}.) -// three columns: a>=1 AND (a,b,c) > (1,2,3) -// -// Note that we are using the short-form for row comparisons. This is defined -// by MySQL. See: http://dev.mysql.com/doc/refman/5.5/en/comparison-operators.html -// -// For row comparisons, (a, b) < (x, y) is equivalent to: -// (a < x) OR ((a = x) AND (b < y)) -// -// -// NOTE: If there is more than one column, we add an extra clause for the -// first column because older MySQL versions seem to do a full table scan -// when we use the short-form. With the additional clause we skip the full -// table scan up the primary key we're interested it. -func greaterThanTupleWhereClause(columns []string, row []sqltypes.Value) []string { - var clauses []string - - // Additional clause on the first column for multi-columns. - if len(columns) > 1 { - var b strings.Builder - sqlescape.WriteEscapeID(&b, columns[0]) - b.WriteString(">=") - row[0].EncodeSQL(&b) - clauses = append(clauses, b.String()) - } - - var b strings.Builder - // List of columns. - if len(columns) > 1 { - b.WriteByte('(') - } - b.WriteString(strings.Join(escapeAll(columns), ",")) - if len(columns) > 1 { - b.WriteByte(')') - } - - // Operator. - b.WriteString(">") - - // List of values. - if len(columns) > 1 { - b.WriteByte('(') - } - for i := 0; i < len(columns); i++ { - if i != 0 { - b.WriteByte(',') - } - row[i].EncodeSQL(&b) - } - if len(columns) > 1 { - b.WriteByte(')') - } - clauses = append(clauses, b.String()) - - return clauses -} diff --git a/go/vt/worker/restartable_result_reader_test.go b/go/vt/worker/restartable_result_reader_test.go deleted file mode 100644 index a34a59499e5..00000000000 --- a/go/vt/worker/restartable_result_reader_test.go +++ /dev/null @@ -1,217 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package worker - -import ( - "errors" - "flag" - "reflect" - "strings" - "testing" - - "context" - - "vitess.io/vitess/go/sqltypes" - "vitess.io/vitess/go/vt/grpcclient" - "vitess.io/vitess/go/vt/logutil" - "vitess.io/vitess/go/vt/topo/memorytopo" - "vitess.io/vitess/go/vt/vttablet/queryservice" - "vitess.io/vitess/go/vt/vttablet/tabletconn" - "vitess.io/vitess/go/vt/vttablet/tmclient" - "vitess.io/vitess/go/vt/wrangler" - - tabletmanagerdatapb "vitess.io/vitess/go/vt/proto/tabletmanagerdata" - topodatapb "vitess.io/vitess/go/vt/proto/topodata" -) - -func TestGreaterThanTupleWhereClause(t *testing.T) { - testcases := []struct { - columns []string - row []sqltypes.Value - want []string - }{ - { - columns: []string{"a"}, - row: []sqltypes.Value{sqltypes.NewInt64(1)}, - want: []string{"`a`>1"}, - }, - { - columns: []string{"a", "b"}, - row: []sqltypes.Value{ - sqltypes.NewInt64(1), - sqltypes.TestValue(sqltypes.Float32, "2.1"), - }, - want: []string{"`a`>=1", "(`a`,`b`)>(1,2.1)"}, - }, - { - columns: []string{"a", "b", "c"}, - row: []sqltypes.Value{ - sqltypes.NewInt64(1), - sqltypes.TestValue(sqltypes.Float32, "2.1"), - sqltypes.NewVarChar("Bär"), - }, - want: []string{"`a`>=1", "(`a`,`b`,`c`)>(1,2.1,'Bär')"}, - }, - } - - for _, tc := range testcases { - got := greaterThanTupleWhereClause(tc.columns, tc.row) - if !reflect.DeepEqual(got, tc.want) { - t.Errorf("greaterThanTupleWhereClause(%v, %v) = %v, want = %v", tc.columns, tc.row, got, tc.want) - } - } -} - -func TestGenerateQuery(t *testing.T) { - testcases := []struct { - desc string - start sqltypes.Value - end sqltypes.Value - table string - columns []string - primaryKeyColumns []string - lastRow []sqltypes.Value - want string - }{ - { - desc: "start and end defined", - start: sqltypes.NewInt64(11), - end: sqltypes.NewInt64(26), - table: "t1", - columns: []string{"a", "msg1", "msg2"}, - primaryKeyColumns: []string{"a"}, - want: "SELECT `a`,`msg1`,`msg2` FROM `t1` WHERE `a`>=11 AND `a`<26 ORDER BY `a`", - }, - { - desc: "only end defined", - end: sqltypes.NewInt64(26), - table: "t1", - columns: []string{"a", "msg1", "msg2"}, - primaryKeyColumns: []string{"a"}, - want: "SELECT `a`,`msg1`,`msg2` FROM `t1` WHERE `a`<26 ORDER BY `a`", - }, - { - desc: "only start defined", - start: sqltypes.NewInt64(11), - table: "t1", - columns: []string{"a", "msg1", "msg2"}, - primaryKeyColumns: []string{"a"}, - want: "SELECT `a`,`msg1`,`msg2` FROM `t1` WHERE `a`>=11 ORDER BY `a`", - }, - { - desc: "neither start nor end defined", - table: "t1", - columns: []string{"a", "msg1", "msg2"}, - primaryKeyColumns: []string{"a"}, - want: "SELECT `a`,`msg1`,`msg2` FROM `t1` ORDER BY `a`", - }, - { - desc: "neither start nor end defined and no primary key", - table: "t1", - columns: []string{"a", "msg1", "msg2"}, - primaryKeyColumns: []string{}, - want: "SELECT `a`,`msg1`,`msg2` FROM `t1`", - }, - { - desc: "start and end defined (multi-column primary key)", - start: sqltypes.NewInt64(11), - end: sqltypes.NewInt64(26), - table: "t1", - columns: []string{"a", "b", "msg1", "msg2"}, - primaryKeyColumns: []string{"a", "b"}, - want: "SELECT `a`,`b`,`msg1`,`msg2` FROM `t1` WHERE `a`>=11 AND `a`<26 ORDER BY `a`,`b`", - }, - { - desc: "start overridden by last row (multi-column primary key)", - start: sqltypes.NewInt64(11), - end: sqltypes.NewInt64(26), - table: "t1", - columns: []string{"a", "b", "msg1", "msg2"}, - primaryKeyColumns: []string{"a", "b"}, - lastRow: []sqltypes.Value{ - sqltypes.NewInt64(1), - sqltypes.NewInt64(2), - }, - want: "SELECT `a`,`b`,`msg1`,`msg2` FROM `t1` WHERE `a`>=1 AND (`a`,`b`)>(1,2) AND `a`<26 ORDER BY `a`,`b`", - }, - { - desc: "no start or end defined but last row (multi-column primary key)", - table: "t1", - columns: []string{"a", "b", "msg1", "msg2"}, - primaryKeyColumns: []string{"a", "b"}, - lastRow: []sqltypes.Value{ - sqltypes.NewInt64(1), - sqltypes.NewInt64(2), - }, - want: "SELECT `a`,`b`,`msg1`,`msg2` FROM `t1` WHERE `a`>=1 AND (`a`,`b`)>(1,2) ORDER BY `a`,`b`", - }, - } - - for _, tc := range testcases { - r := RestartableResultReader{ - chunk: chunk{tc.start, tc.end, 1, 1}, - td: &tabletmanagerdatapb.TableDefinition{ - Name: tc.table, - Columns: tc.columns, - PrimaryKeyColumns: tc.primaryKeyColumns, - }, - lastRow: tc.lastRow, - } - r.generateQuery() - got := r.query - if got != tc.want { - t.Errorf("testcase = %v: generateQuery(chunk=%v, pk=%v, lastRow=%v) = %v, want = %v", tc.desc, r.chunk, r.td.PrimaryKeyColumns, r.lastRow, got, tc.want) - } - } -} - -// TestNewRestartableResultReader tests the correct error handling e.g. -// if the connection to a tablet fails due to a canceled context. -func TestNewRestartableResultReader(t *testing.T) { - wantErr := errors.New("restartable_result_reader_test.go: context canceled") - - tabletconn.RegisterDialer("fake_dialer", func(tablet *topodatapb.Tablet, failFast grpcclient.FailFast) (queryservice.QueryService, error) { - return nil, wantErr - }) - protocol := flag.CommandLine.Lookup("tablet_protocol").Value.String() - flag.Set("tablet_protocol", "fake_dialer") - // Restore the previous flag value after the test. - defer flag.Set("tablet_protocol", protocol) - - // Create dependencies e.g. a "singleTabletProvider" instance. - ts := memorytopo.NewServer("cell1") - wr := wrangler.New(logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient()) - alias := &topodatapb.TabletAlias{ - Cell: "cell1", - Uid: 1, - } - tablet := &topodatapb.Tablet{ - Keyspace: "ks1", - Shard: "-80", - Alias: alias, - } - ctx := context.Background() - if err := ts.CreateTablet(ctx, tablet); err != nil { - t.Fatalf("CreateTablet failed: %v", err) - } - tp := newSingleTabletProvider(ctx, ts, alias) - - _, err := NewRestartableResultReader(ctx, wr.Logger(), tp, nil /* td */, chunk{}, false) - if err == nil || !strings.Contains(err.Error(), wantErr.Error()) { - t.Fatalf("NewRestartableResultReader() should have failed because the context is canceled: %v", err) - } -} diff --git a/go/vt/worker/result_merger.go b/go/vt/worker/result_merger.go deleted file mode 100644 index 21a28c6f62f..00000000000 --- a/go/vt/worker/result_merger.go +++ /dev/null @@ -1,310 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package worker - -import ( - "container/heap" - "fmt" - "io" - - "google.golang.org/protobuf/proto" - - "context" - - "vitess.io/vitess/go/vt/proto/vtrpc" - "vitess.io/vitess/go/vt/vterrors" - - "vitess.io/vitess/go/sqltypes" - - querypb "vitess.io/vitess/go/vt/proto/query" -) - -// ResultSizeRows specifies how many rows should be merged together per -// returned Result. Higher values will improve the performance of the overall -// pipeline but increase the memory usage. -// The current value of 64 rows tries to aim at a total size of 4k bytes -// for the returned Result (i.e. we assume an average row size of 64 bytes). -const ResultSizeRows = 64 - -// ResultMerger returns a sorted stream of multiple ResultReader input streams. -// The output stream will be sorted by ascending primary key order. -// It implements the ResultReader interface. -type ResultMerger struct { - inputs []ResultReader - allInputs []ResultReader - fields []*querypb.Field - // output is the buffer of merged rows. Once it's full, we'll return it in - // Next() (wrapped in a sqltypes.Result). - output [][]sqltypes.Value - // nextRowHeap is a priority queue whose min value always points to the next - // row which should be merged into "output". See Next() for details. - nextRowHeap *nextRowHeap - // lastRowReaderDrained becomes true when there is only one input ResultReader - // left and its respective RowReader stopped after the current Result. From - // there on, we can switch to an "endgame" mode where we can read the Result - // directly from the input instead of reading one row at a time. - lastRowReaderDrained bool -} - -// NewResultMerger returns a new ResultMerger. -func NewResultMerger(inputs []ResultReader, pkFieldCount int) (*ResultMerger, error) { - if len(inputs) < 2 { - panic("ResultMerger requires at least two ResultReaders as input") - } - - fields := inputs[0].Fields() - - if err := checkFieldsEqual(fields, inputs); err != nil { - return nil, err - } - - err := CheckValidTypesForResultMerger(fields, pkFieldCount) - if err != nil { - return nil, vterrors.Errorf(vtrpc.Code_FAILED_PRECONDITION, "invalid PK types for ResultMerger. Use the vtworker SplitClone command instead. %v", err.Error()) - } - - // Initialize the priority queue with all input ResultReader which have at - // least one row. - var activeInputs []ResultReader - nextRowHeap := newNextRowHeap(fields, pkFieldCount) - for i, input := range inputs { - nextRow := newNextRow(input) - if err := nextRow.next(); err != nil { - if err == io.EOF { - continue - } - return nil, vterrors.Wrapf(err, "failed to read from input at index: %v ResultReader: %v err", i, input) - } - activeInputs = append(activeInputs, input) - heap.Push(nextRowHeap, nextRow) - } - - rm := &ResultMerger{ - inputs: activeInputs, - allInputs: inputs, - fields: fields, - nextRowHeap: nextRowHeap, - } - rm.reset() - return rm, nil -} - -// CheckValidTypesForResultMerger returns an error if the provided fields are not compatible with how ResultMerger works -func CheckValidTypesForResultMerger(fields []*querypb.Field, pkFieldCount int) error { - for i := 0; i < pkFieldCount; i++ { - typ := fields[i].Type - if !sqltypes.IsIntegral(typ) && !sqltypes.IsFloat(typ) && !sqltypes.IsBinary(typ) { - return vterrors.Errorf(vtrpc.Code_FAILED_PRECONDITION, "unsupported type: %v cannot compare fields with this type", typ) - } - } - return nil -} - -// Fields returns the field information for the columns in the result. -// It is part of the ResultReader interface. -func (rm *ResultMerger) Fields() []*querypb.Field { - return rm.fields -} - -// Next returns the next Result in the sorted, merged stream. -// It is part of the ResultReader interface. -func (rm *ResultMerger) Next() (*sqltypes.Result, error) { - // All input readers were consumed during a merge. Return end of stream. - if len(rm.inputs) == 0 { - return nil, io.EOF - } - // Endgame mode if there is exactly one input left. - if len(rm.inputs) == 1 && rm.lastRowReaderDrained { - return rm.inputs[0].Next() - } - - // Current output buffer is not full and there is more than one input left. - // Keep merging rows from the inputs using the priority queue. - for len(rm.output) < ResultSizeRows && len(rm.inputs) != 0 { - // Get the next smallest row. - next := heap.Pop(rm.nextRowHeap).(*nextRow) - // Add it to the output. - rm.output = append(rm.output, next.row) - // Check if the input we just popped has more rows. - if err := next.next(); err != nil { - if err == io.EOF { - // No more rows. Delete this input ResultReader from "inputs". - rm.deleteInput(next.input) - - if len(rm.inputs) == 1 { - // We just deleted the second last ResultReader from "inputs" i.e. - // only one input is left. Tell the last input's RowReader to stop - // processing new Results after the current Result. Once we read the - // remaining rows, we can switch to the endgame mode where we read the - // Result directly from the input. - lastNextRow := heap.Pop(rm.nextRowHeap).(*nextRow) - lastNextRow.rowReader.StopAfterCurrentResult() - heap.Push(rm.nextRowHeap, lastNextRow) - } - - // Do not add back the deleted input to the priority queue. - continue - } else if err == ErrStoppedRowReader { - // Endgame mode: We just consumed all rows from the last input. - // Switch to a faster mode where we read Results from the input instead - // of reading rows from the RowReader. - rm.lastRowReaderDrained = true - break - } else { - return nil, vterrors.Wrapf(err, "failed to read from input ResultReader: %v err", next.input) - } - } - // Input has more rows. Add it back to the priority queue. - heap.Push(rm.nextRowHeap, next) - } - - result := &sqltypes.Result{ - Fields: rm.fields, - Rows: rm.output, - } - rm.reset() - - return result, nil -} - -// Close closes all inputs -func (rm *ResultMerger) Close(ctx context.Context) { - for _, i := range rm.allInputs { - i.Close(ctx) - } -} - -func (rm *ResultMerger) deleteInput(deleteMe ResultReader) { - for i, input := range rm.inputs { - if input == deleteMe { - rm.inputs = append(rm.inputs[:i], rm.inputs[i+1:]...) - break - } - } -} - -func (rm *ResultMerger) reset() { - // Allocate the full array at once. - rm.output = make([][]sqltypes.Value, 0, ResultSizeRows) -} - -func equalFields(a, b []*querypb.Field) bool { - if len(a) != len(b) { - return false - } - for i, aField := range a { - bField := b[i] - if !proto.Equal(aField, bField) { - return false - } - } - return true -} - -func checkFieldsEqual(fields []*querypb.Field, inputs []ResultReader) error { - for i := 1; i < len(inputs); i++ { - otherFields := inputs[i].Fields() - if !equalFields(fields, otherFields) { - return vterrors.Errorf(vtrpc.Code_FAILED_PRECONDITION, "input ResultReaders have conflicting Fields data: ResultReader[0]: %v != ResultReader[%d]: %v", fields, i, otherFields) - } - } - return nil -} - -// nextRow is an entry in the nextRowHeap priority queue. -// It stores the current row which should be used for sorting, the input and the -// input's RowReader which will allow to move forward to the next row. -type nextRow struct { - input ResultReader - // rowReader allows to consume "input" one row at a time. - rowReader *RowReader - row []sqltypes.Value -} - -func newNextRow(input ResultReader) *nextRow { - return &nextRow{ - input: input, - rowReader: NewRowReader(input), - } -} - -// next reads the next row from the input. It returns io.EOF if the input stream -// has ended. -func (n *nextRow) next() error { - var err error - n.row, err = n.rowReader.Next() - if err != nil { - return err - } - if n.row == nil { - return io.EOF - } - return nil -} - -// nextRowHeap implements a priority queue which allows to sort multiple -// input ResultReader by their smallest row (in terms of primary key order). -// It's intended to be used as follows: -// - pop the smallest value -// - advance the popped input to the next row -// - push the input with the new row back (unless the stream has ended) -// See ResultMerger.Next() for details. -type nextRowHeap struct { - // fields is required by the CompareRows function. - fields []*querypb.Field - // pkFieldCount is the number of columns in the primary key. - // We assume that the first columns in the row belong to the primary key and - // we only compare them. - pkFieldCount int - // nextRowByInputs is the underlying storage of the priority queue. - nextRowByInputs []*nextRow -} - -func newNextRowHeap(fields []*querypb.Field, pkFieldCount int) *nextRowHeap { - return &nextRowHeap{ - fields: fields, - pkFieldCount: pkFieldCount, - } -} - -func (h nextRowHeap) Len() int { return len(h.nextRowByInputs) } -func (h nextRowHeap) Less(i, j int) bool { - result, err := CompareRows(h.fields, h.pkFieldCount, h.nextRowByInputs[i].row, h.nextRowByInputs[j].row) - if err != nil { - panic(fmt.Sprintf("failed to compare two rows: %v vs. %v err: %v", h.nextRowByInputs[i].row, h.nextRowByInputs[j].row, err)) - } - return result == -1 -} -func (h nextRowHeap) Swap(i, j int) { - h.nextRowByInputs[i], h.nextRowByInputs[j] = h.nextRowByInputs[j], h.nextRowByInputs[i] -} - -// Push adds x as element Len(). -// It is part of the container/heap.Interface interface. -func (h *nextRowHeap) Push(x any) { - h.nextRowByInputs = append(h.nextRowByInputs, x.(*nextRow)) -} - -// Push removes and returns element Len()-1. -// It is part of the container/heap.Interface interface. -func (h *nextRowHeap) Pop() any { - old := h.nextRowByInputs - n := len(old) - x := old[n-1] - h.nextRowByInputs = old[0 : n-1] - return x -} diff --git a/go/vt/worker/result_merger_test.go b/go/vt/worker/result_merger_test.go deleted file mode 100644 index b1d4ef501e7..00000000000 --- a/go/vt/worker/result_merger_test.go +++ /dev/null @@ -1,486 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package worker - -import ( - "fmt" - "io" - "reflect" - "testing" - "time" - - "context" - - "vitess.io/vitess/go/sqltypes" - - querypb "vitess.io/vitess/go/vt/proto/query" -) - -// singlePk presents a table with a primary key that is a single integer column. -var singlePk = []*querypb.Field{{Name: "id", Type: sqltypes.Int32}, {Name: "msg", Type: sqltypes.Char}} - -// multiPk presents a table with a primary key with multiple (two) integer columns. -var multiPk = []*querypb.Field{{Name: "id", Type: sqltypes.Int32}, {Name: "sub_id", Type: sqltypes.Int32}, {Name: "msg", Type: sqltypes.Char}} - -// fakeResultReader returns generated Result structs. -// It implements the ResultReader interface. -type fakeResultReader struct { - fields []*querypb.Field - createRow rowFactory - // rangeInterval is the interval between two range starts. - rangeInterval int - // rangeLength is the length of *our* part within the range. It's usually shorter than rangeInterval. - rangeLength int - rowsPerResult int - rowsTotal int - - // nextRangeStart is the start of the next range. It points at the start of - // *our* part of the range i.e. different fakeResultReader have different - // range starts. - nextRangeStart int - // currentIndex is the current index within the current range. - currentIndex int - rowsReturned int - closed bool -} - -// newFakeResultReader returns a new FakeResultReader. -// -// "distribution" specifies how many consecutive rows each input should own. -// For example, [10, 1] means that the first input has the rows (more -// precisely primary keys) from 1 to 10, second input has 11, first input has -// 12 to 21, second input has 22 and so on. -// -// "index" specifies the index in "distribution" i.e. which input we are. -// -// "iterations" specifies how often the distribution should be repeated e.g. -// a [1, 1] distribution and 10 iterations means that each input will return -// 1*10 rows. -func newFakeResultReader(fields []*querypb.Field, index int, distribution []int, iterations int) *fakeResultReader { - createRow := createRowSinglePk - if reflect.DeepEqual(fields, multiPk) { - createRow = createRowMultiPk - } - - // Compute our start within the range. - rangeStart := 0 - for i, rangeLength := range distribution { - if index == i { - break - } - rangeStart += rangeLength - } - - // Compute the total length of the range across all inputs. - rangeInterval := 0 - for _, rangeLength := range distribution { - rangeInterval += rangeLength - } - - rowsTotal := iterations * distribution[index] - rowsPerResult := ResultSizeRows - if rowsPerResult > rowsTotal { - rowsPerResult = rowsTotal - } - - return &fakeResultReader{ - fields: fields, - createRow: createRow, - rangeInterval: rangeInterval, - rangeLength: distribution[index], - rowsPerResult: rowsPerResult, - rowsTotal: rowsTotal, - nextRangeStart: rangeStart, - } -} - -// Fields returns the field information. It is part of the ResultReader interface. -func (f *fakeResultReader) Fields() []*querypb.Field { - return f.fields -} - -// Close closes nothing -func (f *fakeResultReader) Close(ctx context.Context) { - f.closed = true -} - -// Next returns the next fake result. It is part of the ResultReader interface. -func (f *fakeResultReader) Next() (*sqltypes.Result, error) { - if f.rowsReturned == f.rowsTotal { - return nil, io.EOF - } - - var rows [][]sqltypes.Value - rowsCount := 0 - for rowsCount < f.rowsPerResult { - if f.rowsReturned == f.rowsTotal { - break - } - - // We covered our part of the range. Move to the start of the next range. - if f.currentIndex == f.rangeLength { - f.currentIndex = 0 - f.nextRangeStart += f.rangeInterval - } - - id := f.nextRangeStart + f.currentIndex - rows = append(rows, f.createRow(id)) - - rowsCount++ - f.currentIndex++ - f.rowsReturned++ - } - - return &sqltypes.Result{ - Fields: f.fields, - RowsAffected: uint64(rowsCount), - Rows: rows, - }, nil -} - -type rowFactory func(id int) []sqltypes.Value - -func createRowSinglePk(id int) []sqltypes.Value { - idValue := sqltypes.NewInt64(int64(id)) - return []sqltypes.Value{ - idValue, - sqltypes.NewVarBinary(fmt.Sprintf("msg %d", id)), - } -} - -func createRowMultiPk(id int) []sqltypes.Value { - // Map id from the one dimensional space to a two-dimensional space. - // Examples: 0, 1, 2, 3, 4 => (0, 0), (0, 1), (1, 0), (1, 1), (2, 0) - newID := id / 2 - subID := id % 2 - return []sqltypes.Value{ - sqltypes.NewInt64(int64(newID)), - sqltypes.NewInt64(int64(subID)), - sqltypes.NewVarBinary(fmt.Sprintf("msg %d", id)), - } -} - -// mergedResults returns Result(s) with rows in the range [0, rowsTotal). -func mergedResults(fields []*querypb.Field, rowsTotal, rowsPerResult int) []*sqltypes.Result { - createRow := createRowSinglePk - if reflect.DeepEqual(fields, multiPk) { - createRow = createRowMultiPk - } - if rowsPerResult > rowsTotal { - rowsPerResult = rowsTotal - } - var results []*sqltypes.Result - var rows [][]sqltypes.Value - for id := 0; id < rowsTotal; id++ { - rows = append(rows, createRow(id)) - - // Last row in the Result or last row in total. - if id%rowsPerResult == (rowsPerResult-1) || id == (rowsTotal-1) { - results = append(results, &sqltypes.Result{ - Fields: fields, - Rows: rows, - }) - rows = make([][]sqltypes.Value, 0) - } - } - return results -} - -// TestResultMerger tests the ResultMerger component by using multiple -// FakeResultReader as input and comparing the output. -// Rows in the test are generated with a single or multi-column primary key. -// The primary key column(s) have an integer type and start at 0. -func TestResultMerger(t *testing.T) { - testcases := []struct { - desc string - inputs []ResultReader - want []*sqltypes.Result - multiPk bool - }{ - { - desc: "2 inputs, 2 rows, even distribution", - inputs: []ResultReader{ - newFakeResultReader(singlePk, 0, []int{1, 1}, 1), - newFakeResultReader(singlePk, 1, []int{1, 1}, 1), - }, - want: mergedResults(singlePk, 2, ResultSizeRows), - }, - { - desc: "2 inputs, enough rows such that ResultMerger must chunk its output into 4 results", - inputs: []ResultReader{ - newFakeResultReader(singlePk, 0, []int{1, 1}, 2000), - newFakeResultReader(singlePk, 1, []int{1, 1}, 2000), - }, - want: mergedResults(singlePk, 4000, ResultSizeRows), - }, - { - desc: "2 inputs, last Result returned by ResultMerger is partial i.e. it has less than 'ResultSizeRows' rows", - inputs: []ResultReader{ - newFakeResultReader(singlePk, 0, []int{ResultSizeRows + 1, ResultSizeRows}, 1), - newFakeResultReader(singlePk, 1, []int{ResultSizeRows + 1, ResultSizeRows}, 1), - }, - want: mergedResults(singlePk, 2*ResultSizeRows+1, ResultSizeRows), - }, - { - desc: "2 inputs, 11 rows, 10:1 distribution", - inputs: []ResultReader{ - newFakeResultReader(singlePk, 0, []int{10, 1}, 1), - newFakeResultReader(singlePk, 1, []int{10, 1}, 1), - }, - want: mergedResults(singlePk, 11, ResultSizeRows), - }, - { - desc: "2 inputs, 11 rows, 1:10 distribution", - inputs: []ResultReader{ - newFakeResultReader(singlePk, 0, []int{1, 10}, 1), - newFakeResultReader(singlePk, 1, []int{1, 10}, 1), - }, - want: mergedResults(singlePk, 11, ResultSizeRows), - }, - { - desc: "3 inputs, 3 rows, even distribution", - inputs: []ResultReader{ - newFakeResultReader(singlePk, 0, []int{1, 1, 1}, 1), - newFakeResultReader(singlePk, 1, []int{1, 1, 1}, 1), - newFakeResultReader(singlePk, 2, []int{1, 1, 1}, 1), - }, - want: mergedResults(singlePk, 3, ResultSizeRows), - }, - { - desc: "2 inputs, 4 rows, multi-column primary key", - inputs: []ResultReader{ - // Note: The order of the inputs is reversed on purpose to verify that - // the sort also considers the "sub_id" column. - newFakeResultReader(multiPk, 1, []int{1, 1}, 2), - newFakeResultReader(multiPk, 0, []int{1, 1}, 2), - }, - want: mergedResults(multiPk, 4, ResultSizeRows), - multiPk: true, - }, - { - desc: "2 inputs, 4 rows including 1 duplicate row", - // NOTE: In practice, this case should not happen because the inputs should be disjoint. - inputs: []ResultReader{ - newFakeResultReader(singlePk, 0, []int{2, 1}, 1), - newFakeResultReader(singlePk, 1, []int{1, 2}, 1), - }, - want: func() []*sqltypes.Result { - // Rows: 0, 1, 2 - results := mergedResults(singlePk, 3, ResultSizeRows) - // Duplicate Row 1. New Rows: 0, 1, 1, 2 - results[0].Rows = append(results[0].Rows[:2], results[0].Rows[1:]...) - return results - }(), - }, - { - desc: "2 inputs, 2 rows, row with id 1 is 'missing' i.e. a gap", - inputs: []ResultReader{ - newFakeResultReader(singlePk, 0, []int{1, 1, 1}, 1), - newFakeResultReader(singlePk, 2, []int{1, 1, 1}, 1), - }, - want: func() []*sqltypes.Result { - // Rows: 0, 1, 2 - results := mergedResults(singlePk, 3, ResultSizeRows) - // Remove row 1. New Rows: 0, 2 - results[0].Rows = append(results[0].Rows[:1], results[0].Rows[2:]...) - return results - }(), - }, - } - - for _, tc := range testcases { - t.Run(fmt.Sprintf("checking testcase: %v", tc.desc), func(inner *testing.T) { - pkFieldCount := 1 - if tc.multiPk { - pkFieldCount = 2 - } - rm, err := NewResultMerger(tc.inputs, pkFieldCount) - if err != nil { - inner.Fatal(err) - } - - // Consume all merged Results. - var got []*sqltypes.Result - for { - result, err := rm.Next() - if err != nil { - if err == io.EOF { - break - } else { - inner.Fatal(err) - } - } - got = append(got, result) - } - - rm.Close(context.Background()) - - if !reflect.DeepEqual(got, tc.want) { - for i := range got { - if i == len(tc.want) { - // got has more Results than want. Avoid index out of range errors. - break - } - if got[i].RowsAffected != tc.want[i].RowsAffected { - inner.Logf("deviating RowsAffected value for Result at index: %v got = %v, want = %v", i, got[i].RowsAffected, tc.want[i].RowsAffected) - } - inner.Logf("deviating Rows for Result at index: %v got = %v, want = %v", i, got[i].Rows, tc.want[i].Rows) - } - if len(tc.want)-len(got) > 0 { - for i := len(got); i < len(tc.want); i++ { - inner.Logf("missing Result in got: %v", tc.want[i].Rows) - } - } - if len(got)-len(tc.want) > 0 { - for i := len(tc.want); i < len(got); i++ { - inner.Logf("unnecessary extra Result in got: %v", got[i].Rows) - } - } - inner.Fatalf("ResultMerger testcase '%v' failed. See output above for different rows.", tc.desc) - } - - for _, x := range tc.inputs { - fake := x.(*fakeResultReader) - if !fake.closed { - inner.Fatal("expected inputs to be closed by now") - } - } - }) - } -} - -// memoryResultReader is a ResultReader implementation which fully consumes -// the stream of another ResultReader and stores it in memory. -// It is used in benchmarks to exclude the CPU time and memory allocations -// of e.g. the fakeResultReader implementation. -type memoryResultReader struct { - fields []*querypb.Field - results []*sqltypes.Result - currentIndex int -} - -func newMemoryResultReader(input ResultReader) *memoryResultReader { - m := &memoryResultReader{ - fields: input.Fields(), - } - for { - result, err := input.Next() - if err != nil { - if err == io.EOF { - return m - } - panic(err) - } - m.results = append(m.results, result) - } -} - -func (m *memoryResultReader) Fields() []*querypb.Field { - return m.fields -} - -func (m *memoryResultReader) Next() (*sqltypes.Result, error) { - if m.currentIndex == len(m.results) { - return nil, io.EOF - } - result := m.results[m.currentIndex] - m.currentIndex++ - return result, nil -} - -func (m *memoryResultReader) Close(ctx context.Context) { - // intentionally blank. we have nothing we need to close -} - -// benchmarkResult is a package level variable whose sole purpose is to -// reference output from the Benchmark* functions below. -// This was suggested by http://dave.cheney.net/2013/06/30/how-to-write-benchmarks-in-go -// to avoid that a compiler optimization eliminates our function call away. -var benchmarkResult *sqltypes.Result - -func BenchmarkResultMerger_2Inputs_1to1(b *testing.B) { - benchmarkResultMerger(b, []int{1, 1}) -} - -func BenchmarkResultMerger_2Inputs_1000to1000(b *testing.B) { - benchmarkResultMerger(b, []int{1000, 1000}) -} - -func BenchmarkResultMerger_4Inputs_1to1(b *testing.B) { - benchmarkResultMerger(b, []int{1, 1, 1, 1}) -} - -func BenchmarkResultMerger_4Inputs_1000to1000(b *testing.B) { - benchmarkResultMerger(b, []int{1000, 1000, 1000, 1000}) -} - -func BenchmarkResultMerger_8Inputs_1to1(b *testing.B) { - benchmarkResultMerger(b, []int{1, 1, 1, 1, 1, 1, 1, 1}) -} - -func BenchmarkResultMerger_8Inputs_1000to1000(b *testing.B) { - benchmarkResultMerger(b, []int{1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000}) -} - -func benchmarkResultMerger(b *testing.B, distribution []int) { - if b.N < len(distribution) { - b.Logf("b.N = %8d: skipping benchmark because b.N is lower than the number of inputs: %v", b.N, len(distribution)) - return - } - - start := time.Now() - - inputs := make([]ResultReader, len(distribution)) - actualRowCount := 0 - for i := range distribution { - rowsPerInput := b.N / len(distribution) - if rowsPerInput < distribution[i] { - b.Logf("b.N = %8d: adjusting the number of rows per distribution from: %v to: %v", b.N, distribution[i], rowsPerInput) - distribution[i] = rowsPerInput - } - iterations := b.N / distribution[i] / len(distribution) - if iterations == 0 { - b.Logf("b.N = %8d: skipping benchmark because b.N is lower than the number of rows per input: %v", b.N, distribution[i]) - return - } - actualRowCount += distribution[i] * iterations - inputs[i] = newMemoryResultReader( - newFakeResultReader(singlePk, i, distribution, iterations)) - } - rm, err := NewResultMerger(inputs, 1 /* pkFieldCount */) - if err != nil { - b.Fatal(err) - } - b.Logf("b.N = %8d: Preloaded %v ResultReader with %10d rows total. Took %4.1f seconds.", b.N, len(distribution), actualRowCount, time.Since(start).Seconds()) - - b.ResetTimer() - - // Merge all rows. - var lastResult *sqltypes.Result - for { - result, err := rm.Next() - if err != nil { - if err == io.EOF { - break - } else { - b.Fatal(err) - } - } - lastResult = result - } - benchmarkResult = lastResult -} diff --git a/go/vt/worker/result_reader.go b/go/vt/worker/result_reader.go deleted file mode 100644 index 2f6283b0902..00000000000 --- a/go/vt/worker/result_reader.go +++ /dev/null @@ -1,45 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package worker - -import ( - "context" - - "vitess.io/vitess/go/sqltypes" - - querypb "vitess.io/vitess/go/vt/proto/query" -) - -// ResultReader is an advanced version of sqltypes.ResultStream. -// In addition to the streamed Result messages (which contain a set of rows), -// it will expose the Fields (columns information) of the result separately. -// -// Note that some code in the worker package checks if instances of ResultReader -// are equal. In consequence, any ResultReader implementation must always use -// pointer receivers. This way, implementations are always referred by their -// pointer type and the equal comparison of ResultReader instances behaves as -// expected. -type ResultReader interface { - // Fields returns the field information for the columns in the result. - Fields() []*querypb.Field - - // Next is identical to sqltypes.ResultStream.Recv(). - // It returns the next result on the stream. - // It will return io.EOF if the stream ended. - Next() (*sqltypes.Result, error) - Close(ctx context.Context) -} diff --git a/go/vt/worker/row_aggregator.go b/go/vt/worker/row_aggregator.go deleted file mode 100644 index 854465acbe6..00000000000 --- a/go/vt/worker/row_aggregator.go +++ /dev/null @@ -1,301 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package worker - -import ( - "fmt" - "strings" - - "context" - - "vitess.io/vitess/go/vt/vterrors" - - "vitess.io/vitess/go/sqlescape" - "vitess.io/vitess/go/sqltypes" - "vitess.io/vitess/go/stats" - - tabletmanagerdatapb "vitess.io/vitess/go/vt/proto/tabletmanagerdata" -) - -// RowAggregator aggregates SQL reconciliation statements into one statement. -// Once a limit (maxRows or maxSize) is reached, the statement will be sent to -// the destination's insertChannel. -// RowAggregator is also aware of the type of statement (DiffType) and -// constructs the necessary SQL command based on that. -// Aggregating multiple statements is done to improve the overall performance. -// One RowAggregator instance is specific to one destination shard and diff -// type. -// Important: The SQL statement generation assumes that the fields of the -// provided row are in the same order as "td.Columns". -type RowAggregator struct { - ctx context.Context - maxRows int - maxSize int - insertChannel chan string - td *tabletmanagerdatapb.TableDefinition - diffType DiffType - builder QueryBuilder - statsCounters *stats.CountersWithSingleLabel - - buffer strings.Builder - bufferedRows int -} - -// NewRowAggregator returns a RowAggregator. -// The index of the elements in statCounters must match the elements -// in "DiffTypes" i.e. the first counter is for inserts, second for updates -// and the third for deletes. -func NewRowAggregator(ctx context.Context, maxRows, maxSize int, insertChannel chan string, dbName string, td *tabletmanagerdatapb.TableDefinition, diffType DiffType, statsCounters *stats.CountersWithSingleLabel) *RowAggregator { - // Construct head and tail base commands for the reconciliation statement. - var builder QueryBuilder - switch diffType { - case DiffMissing: - // Example: INSERT INTO test (id, sub_id, msg) VALUES (0, 10, 'a'), (1, 11, 'b') - builder = NewInsertsQueryBuilder(dbName, td) - case DiffNotEqual: - // Example: UPDATE test SET msg='a' WHERE id=0 AND sub_id=10 - builder = NewUpdatesQueryBuilder(dbName, td) - // UPDATE ... SET does not support multiple rows as input. - maxRows = 1 - case DiffExtraneous: - // Example: DELETE FROM test WHERE (id, sub_id) IN ((0, 10), (1, 11)) - builder = NewDeletesQueryBuilder(dbName, td) - default: - panic(fmt.Sprintf("unknown DiffType: %v", diffType)) - } - - return &RowAggregator{ - ctx: ctx, - maxRows: maxRows, - maxSize: maxSize, - insertChannel: insertChannel, - td: td, - diffType: diffType, - builder: builder, - statsCounters: statsCounters, - } -} - -// Add will add a new row which must be reconciled. -// If an error is returned, RowAggregator will be in an undefined state and must -// not be used any longer. -func (ra *RowAggregator) Add(row []sqltypes.Value) error { - if ra.buffer.Len() == 0 { - ra.builder.WriteHead(&ra.buffer) - } - - if ra.bufferedRows >= 1 { - ra.builder.WriteSeparator(&ra.buffer) - } - ra.builder.WriteRow(&ra.buffer, row) - ra.bufferedRows++ - - if ra.bufferedRows >= ra.maxRows || ra.buffer.Len() >= ra.maxSize { - if err := ra.Flush(); err != nil { - return err - } - } - - return nil -} - -// Flush sends out the current aggregation buffer. -func (ra *RowAggregator) Flush() error { - if ra.buffer.Len() == 0 { - // Already flushed. - return nil - } - - ra.builder.WriteTail(&ra.buffer) - // select blocks until sending the SQL succeeded or the context was canceled. - select { - case ra.insertChannel <- ra.buffer.String(): - case <-ra.ctx.Done(): - return vterrors.Wrap(ra.ctx.Err(), "failed to flush RowAggregator and send the query to a writer thread channel") - } - - // Update our statistics. - ra.statsCounters.Add(ra.td.Name, int64(ra.bufferedRows)) - - ra.buffer.Reset() - ra.bufferedRows = 0 - return nil -} - -// QueryBuilder defines for a given reconciliation type how we have to -// build the SQL query for one or more rows. -type QueryBuilder interface { - // WriteHead writes the beginning of the query into the buffer. - WriteHead(*strings.Builder) - // WriteTail writes any required tailing string into the buffer. - WriteTail(*strings.Builder) - // Write the separator between two rows. - WriteSeparator(*strings.Builder) - // Write the row itself. - WriteRow(*strings.Builder, []sqltypes.Value) -} - -// BaseQueryBuilder partially implements the QueryBuilder interface. -// It can be used by other QueryBuilder implementations to avoid repeating -// code. -type BaseQueryBuilder struct { - head string - tail string - separator string -} - -// WriteHead implements the QueryBuilder interface. -func (b *BaseQueryBuilder) WriteHead(buffer *strings.Builder) { - buffer.WriteString(b.head) -} - -// WriteTail implements the QueryBuilder interface. -func (b *BaseQueryBuilder) WriteTail(buffer *strings.Builder) { - buffer.WriteString(b.tail) -} - -// WriteSeparator implements the QueryBuilder interface. -func (b *BaseQueryBuilder) WriteSeparator(buffer *strings.Builder) { - if b.separator == "" { - panic("BaseQueryBuilder.WriteSeparator(): separator not defined") - } - buffer.WriteString(b.separator) -} - -// InsertsQueryBuilder implements the QueryBuilder interface for INSERT queries. -type InsertsQueryBuilder struct { - BaseQueryBuilder -} - -// NewInsertsQueryBuilder creates a new InsertsQueryBuilder. -func NewInsertsQueryBuilder(dbName string, td *tabletmanagerdatapb.TableDefinition) *InsertsQueryBuilder { - // Example: INSERT INTO test (id, sub_id, msg) VALUES (0, 10, 'a'), (1, 11, 'b') - return &InsertsQueryBuilder{ - BaseQueryBuilder{ - head: "INSERT INTO " + sqlescape.EscapeID(dbName) + "." + sqlescape.EscapeID(td.Name) + " (" + strings.Join(escapeAll(td.Columns), ", ") + ") VALUES ", - separator: ",", - }, - } -} - -// WriteRow implements the QueryBuilder interface. -func (*InsertsQueryBuilder) WriteRow(buffer *strings.Builder, row []sqltypes.Value) { - // Example: (0, 10, 'a'), (1, 11, 'b') - buffer.WriteByte('(') - for i, value := range row { - if i > 0 { - buffer.WriteByte(',') - } - value.EncodeSQL(buffer) - } - buffer.WriteByte(')') -} - -// UpdatesQueryBuilder implements the QueryBuilder interface for UPDATE queries. -type UpdatesQueryBuilder struct { - BaseQueryBuilder - td *tabletmanagerdatapb.TableDefinition - // nonPrimaryKeyColumns is td.Columns filtered by td.PrimaryKeyColumns. - // The order of td.Columns is preserved. - nonPrimaryKeyColumns []string -} - -// NewUpdatesQueryBuilder creates a new UpdatesQueryBuilder. -func NewUpdatesQueryBuilder(dbName string, td *tabletmanagerdatapb.TableDefinition) *UpdatesQueryBuilder { - // Example: UPDATE test SET msg='a' WHERE id=0 AND sub_id=10 - // - // Note: We cannot use INSERT INTO ... ON DUPLICATE KEY UPDATE here because - // it's not recommended for tables with more than one unique (i.e. the - // primary key) index. That's because the update function would also be - // triggered if a unique, non-primary key index matches the row. In that - // case, we would update the wrong row (it gets selected by the unique key - // and not the primary key). - return &UpdatesQueryBuilder{ - BaseQueryBuilder: BaseQueryBuilder{ - head: "UPDATE " + sqlescape.EscapeID(dbName) + "." + sqlescape.EscapeID(td.Name) + " SET ", - }, - td: td, - // Build list of non-primary key columns (required for update statements). - nonPrimaryKeyColumns: orderedColumnsWithoutPrimaryKeyColumns(td), - } -} - -// WriteSeparator implements the QueryBuilder interface and overrides -// the BaseQueryBuilder implementation. -func (b *UpdatesQueryBuilder) WriteSeparator(buffer *strings.Builder) { - panic("UpdatesQueryBuilder does not support aggregating multiple rows in one query") -} - -// WriteRow implements the QueryBuilder interface. -func (b *UpdatesQueryBuilder) WriteRow(buffer *strings.Builder, row []sqltypes.Value) { - // Example: msg='a' WHERE id=0 AND sub_id=10 - nonPrimaryOffset := len(b.td.PrimaryKeyColumns) - for i, column := range b.nonPrimaryKeyColumns { - if i > 0 { - buffer.WriteByte(',') - } - sqlescape.WriteEscapeID(buffer, column) - buffer.WriteByte('=') - row[nonPrimaryOffset+i].EncodeSQL(buffer) - } - buffer.WriteString(" WHERE ") - for i, pkColumn := range b.td.PrimaryKeyColumns { - if i > 0 { - buffer.WriteString(" AND ") - } - sqlescape.WriteEscapeID(buffer, pkColumn) - buffer.WriteByte('=') - row[i].EncodeSQL(buffer) - } -} - -// DeletesQueryBuilder implements the QueryBuilder interface for DELETE queries. -type DeletesQueryBuilder struct { - BaseQueryBuilder - td *tabletmanagerdatapb.TableDefinition -} - -// NewDeletesQueryBuilder creates a new DeletesQueryBuilder. -func NewDeletesQueryBuilder(dbName string, td *tabletmanagerdatapb.TableDefinition) *DeletesQueryBuilder { - // Example: DELETE FROM test WHERE (id=0 AND sub_id=10) OR (id=1 AND sub_id=11) - // - // Note that we don't do multi row DELETEs with an IN expression because - // there are reports in the wild that MySQL 5.6 would do a full table scan - // for such a query. (We haven't confirmed this ourselves.) - return &DeletesQueryBuilder{ - BaseQueryBuilder: BaseQueryBuilder{ - head: "DELETE FROM " + sqlescape.EscapeID(dbName) + "." + sqlescape.EscapeID(td.Name) + " WHERE ", - separator: " OR ", - }, - td: td, - } -} - -// WriteRow implements the QueryBuilder interface. -func (b *DeletesQueryBuilder) WriteRow(buffer *strings.Builder, row []sqltypes.Value) { - // Example: (id=0 AND sub_id=10) OR (id=1 AND sub_id=11) - buffer.WriteByte('(') - for i, pkColumn := range b.td.PrimaryKeyColumns { - if i > 0 { - buffer.WriteString(" AND ") - } - sqlescape.WriteEscapeID(buffer, pkColumn) - buffer.WriteByte('=') - row[i].EncodeSQL(buffer) - } - buffer.WriteByte(')') -} diff --git a/go/vt/worker/row_differ.go b/go/vt/worker/row_differ.go deleted file mode 100644 index c32a1ded9b4..00000000000 --- a/go/vt/worker/row_differ.go +++ /dev/null @@ -1,369 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package worker - -import ( - "fmt" - "time" - - "vitess.io/vitess/go/vt/proto/vtrpc" - "vitess.io/vitess/go/vt/vterrors" - - "context" - - "vitess.io/vitess/go/sqltypes" - "vitess.io/vitess/go/stats" - "vitess.io/vitess/go/vt/key" - "vitess.io/vitess/go/vt/topo" - - querypb "vitess.io/vitess/go/vt/proto/query" - tabletmanagerdatapb "vitess.io/vitess/go/vt/proto/tabletmanagerdata" - topodatapb "vitess.io/vitess/go/vt/proto/topodata" -) - -// DiffType specifies why a specific row was found as different when comparing -// a left and right side. -type DiffType int - -const ( - // DiffMissing is returned when the row is missing on the right side. - DiffMissing DiffType = iota - // DiffNotEqual is returned when the row on the left and right side are - // not equal. - DiffNotEqual - // DiffExtraneous is returned when the row exists on the right side, but not - // on the left side. - DiffExtraneous - // DiffEqual is returned when the rows left and right are equal. - DiffEqual -) - -// DiffTypes has the list of available DiffType values, ordered by their value. -var DiffTypes = []DiffType{DiffMissing, DiffNotEqual, DiffExtraneous, DiffEqual} - -// DiffFoundTypes has the list of DiffType values which represent that a -// difference was found. The list is ordered by the values of the types. -var DiffFoundTypes = []DiffType{DiffMissing, DiffNotEqual, DiffExtraneous} - -// RowDiffer2 will compare and reconcile two sides. It assumes that the left -// side is the source of truth and necessary reconciliations have to be applied -// to the right side. -// It also assumes left and right are sorted by ascending primary key. -type RowDiffer2 struct { - left *RowReader - right *RowReader - pkFieldCount int - // tableStatusList is used to report the number of reconciled rows. - tableStatusList *tableStatusList - // tableIndex is the index of the table in the schema. It is required for - // reporting the number of reconciled rows to tableStatusList. - tableIndex int - // router returns for a row to which destination shard index it should go. - router RowRouter - // aggregators are keyed by destination shard and DiffType. - aggregators [][]*RowAggregator - // equalRowsStatsCounters tracks per table how many rows are equal. - equalRowsStatsCounters *stats.CountersWithSingleLabel - // tableName is required to update "equalRowsStatsCounters". - tableName string -} - -// NewRowDiffer2 returns a new RowDiffer2. -// We assume that the indexes of the slice parameters always correspond to the -// same shard e.g. insertChannels[0] refers to destinationShards[0] and so on. -// The column list td.Columns must be have all primary key columns first and -// then the non-primary-key columns. The columns in the rows returned by -// both ResultReader must have the same order as td.Columns. -func NewRowDiffer2(ctx context.Context, left, right ResultReader, td *tabletmanagerdatapb.TableDefinition, tableStatusList *tableStatusList, tableIndex int, - // Parameters required by RowRouter. - destinationShards []*topo.ShardInfo, keyResolver keyspaceIDResolver, - // Parameters required by RowAggregator. - insertChannels []chan string, abort <-chan struct{}, dbNames []string, writeQueryMaxRows, writeQueryMaxSize int, statsCounters []*stats.CountersWithSingleLabel) (*RowDiffer2, error) { - - if len(statsCounters) != len(DiffTypes) { - panic(fmt.Sprintf("statsCounter has the wrong number of elements. got = %v, want = %v", len(statsCounters), len(DiffTypes))) - } - - if err := compareFields(left.Fields(), right.Fields()); err != nil { - return nil, err - } - - // Create a RowAggregator for each destination shard and DiffType. - aggregators := make([][]*RowAggregator, len(destinationShards)) - for i := range destinationShards { - aggregators[i] = make([]*RowAggregator, len(DiffFoundTypes)) - for _, typ := range DiffFoundTypes { - maxRows := writeQueryMaxRows - aggregators[i][typ] = NewRowAggregator(ctx, maxRows, writeQueryMaxSize, - insertChannels[i], dbNames[i], td, typ, statsCounters[typ]) - } - } - - return &RowDiffer2{ - left: NewRowReader(left), - right: NewRowReader(right), - pkFieldCount: len(td.PrimaryKeyColumns), - tableStatusList: tableStatusList, - tableIndex: tableIndex, - router: NewRowRouter(destinationShards, keyResolver), - aggregators: aggregators, - equalRowsStatsCounters: statsCounters[DiffEqual], - tableName: td.Name, - }, nil -} - -func compareFields(left, right []*querypb.Field) error { - if len(left) != len(right) { - return vterrors.Errorf(vtrpc.Code_FAILED_PRECONDITION, "cannot diff inputs with different number of fields: left: %v right: %v", left, right) - } - for i, field := range left { - if field.Type != right[i].Type { - return vterrors.Errorf(vtrpc.Code_FAILED_PRECONDITION, "cannot diff inputs with different types: field %v types are %v and %v", i, field.Type, right[i].Type) - } - } - return nil -} - -// Diff runs the diff and reconcile. -// If an error occurs, it will return and stop. -func (rd *RowDiffer2) Diff() (DiffReport, error) { - var dr DiffReport - var err error - - dr.startingTime = time.Now() - defer dr.ComputeQPS() - - fields := rd.left.Fields() - var left []sqltypes.Value - var right []sqltypes.Value - advanceLeft := true - advanceRight := true - for { - if advanceLeft { - if left, err = rd.left.Next(); err != nil { - return dr, err - } - advanceLeft = false - } - if advanceRight { - if right, err = rd.right.Next(); err != nil { - return dr, err - } - advanceRight = false - } - if left == nil && right == nil { - // No more rows from either side. We're done. - break - } - dr.processedRows++ - if left == nil { - // No more rows on the left side. - // We know we have at least one row on the right side left. - // Delete the row from the destination. - if err := rd.reconcileRow(right, DiffExtraneous); err != nil { - return dr, err - } - dr.extraRowsRight++ - advanceRight = true - continue - } - if right == nil { - // No more rows on the right side. - // We know we have at least one row on the left side left. - // Add the row on the destination. - if err := rd.reconcileRow(left, DiffMissing); err != nil { - return dr, err - } - dr.extraRowsLeft++ - advanceLeft = true - continue - } - - // we have both left and right, compare - f := RowsEqual(left, right) - if f == -1 { - // rows are the same, next - dr.matchingRows++ - advanceLeft = true - advanceRight = true - rd.skipRow() - continue - } - - if f >= rd.pkFieldCount { - // rows have the same primary key, only content is different - dr.mismatchedRows++ - advanceLeft = true - advanceRight = true - // Update the row on the destination. - if err := rd.updateRow(left, right, DiffNotEqual); err != nil { - return dr, err - } - continue - } - - // have to find the 'smallest' row and advance it - c, err := CompareRows(fields, rd.pkFieldCount, left, right) - if err != nil { - return dr, err - } - if c < 0 { - dr.extraRowsLeft++ - advanceLeft = true - // Add the row on the destination. - if err := rd.reconcileRow(left, DiffMissing); err != nil { - return dr, err - } - continue - } else if c > 0 { - dr.extraRowsRight++ - advanceRight = true - // Delete the row from the destination. - if err := rd.reconcileRow(right, DiffExtraneous); err != nil { - return dr, err - } - continue - } - - // Values of the primary key columns were not binary equal but their parsed - // values are. - // This happens when the raw values returned by MySQL were different but - // they became equal after we parsed them into ints/floats - // (due to leading/trailing zeros, for example). So this can happen if MySQL - // is inconsistent in how it prints a given number. - dr.mismatchedRows++ - advanceLeft = true - advanceRight = true - // Update the row on the destination. - if err := rd.updateRow(left, right, DiffNotEqual); err != nil { - return dr, err - } - } - - // Flush all aggregators in case they have buffered queries left. - for i := range rd.aggregators { - for _, aggregator := range rd.aggregators[i] { - if err := aggregator.Flush(); err != nil { - return dr, err - } - } - } - - return dr, nil -} - -// skipRow is used for the DiffType DiffEqual. -// Currently, it only updates the statistics and therefore does not require the -// row as input. -func (rd *RowDiffer2) skipRow() { - rd.equalRowsStatsCounters.Add(rd.tableName, 1) - rd.tableStatusList.addCopiedRows(rd.tableIndex, 1) -} - -// reconcileRow is used for the DiffType DiffMissing and DiffExtraneous. -func (rd *RowDiffer2) reconcileRow(row []sqltypes.Value, typ DiffType) error { - if typ == DiffNotEqual { - panic(fmt.Sprintf("reconcileRow() called with wrong type: %v", typ)) - } - destShardIndex, err := rd.router.Route(row) - if err != nil { - return vterrors.Wrapf(err, "failed to route row (%v) to correct shard", row) - } - - if err := rd.aggregators[destShardIndex][typ].Add(row); err != nil { - return vterrors.Wrap(err, "failed to add row update to RowAggregator") - } - // TODO(mberlin): Add more fine granular stats here. - rd.tableStatusList.addCopiedRows(rd.tableIndex, 1) - return nil -} - -// updateRow is used for the DiffType DiffNotEqual. -// It needs to look at the row of the source (newRow) and destination (oldRow) -// to detect if the keyspace_id has changed in the meantime. -// If that's the case, we cannot UPDATE the row. Instead, we must DELETE -// the old row and INSERT the new row to the respective destination shards. -func (rd *RowDiffer2) updateRow(newRow, oldRow []sqltypes.Value, typ DiffType) error { - if typ != DiffNotEqual { - panic(fmt.Sprintf("updateRow() called with wrong type: %v", typ)) - } - destShardIndexOld, err := rd.router.Route(oldRow) - if err != nil { - return vterrors.Wrapf(err, "failed to route old row (%v) to correct shard", oldRow) - } - destShardIndexNew, err := rd.router.Route(newRow) - if err != nil { - return vterrors.Wrapf(err, "failed to route new row (%v) to correct shard", newRow) - } - - if destShardIndexOld == destShardIndexNew { - // keyspace_id has not changed. Update the row in place on the destination. - if err := rd.aggregators[destShardIndexNew][typ].Add(newRow); err != nil { - return vterrors.Wrap(err, "failed to add row update to RowAggregator (keyspace_id not changed)") - } - } else { - // keyspace_id has changed. Delete the old row and insert the new one. - if err := rd.aggregators[destShardIndexOld][DiffExtraneous].Add(oldRow); err != nil { - return vterrors.Wrap(err, "failed to add row update to RowAggregator (keyspace_id changed, deleting old row)") - } - if err := rd.aggregators[destShardIndexNew][DiffMissing].Add(newRow); err != nil { - return vterrors.Wrap(err, "failed to add row update to RowAggregator (keyspace_id changed, inserting new row)") - } - } - - // TODO(mberlin): Add more fine granular stats here. - rd.tableStatusList.addCopiedRows(rd.tableIndex, 1) - return nil -} - -// RowRouter allows to find out which shard's key range contains a given -// keyspace ID. -type RowRouter struct { - keyResolver keyspaceIDResolver - keyRanges []*topodatapb.KeyRange -} - -// NewRowRouter creates a RowRouter. -// We assume that the key ranges in shardInfo cover the full keyrange i.e. -// for any possible keyspaceID there is a shard we can route to. -func NewRowRouter(shardInfos []*topo.ShardInfo, keyResolver keyspaceIDResolver) RowRouter { - keyRanges := make([]*topodatapb.KeyRange, len(shardInfos)) - for i, si := range shardInfos { - keyRanges[i] = si.KeyRange - } - return RowRouter{keyResolver, keyRanges} -} - -// Route returns which shard (specified by the index of the list of shards -// passed in NewRowRouter) contains the given row. -func (rr *RowRouter) Route(row []sqltypes.Value) (int, error) { - if len(rr.keyRanges) == 1 { - // Fast path when there is only one destination shard. - return 0, nil - } - - k, err := rr.keyResolver.keyspaceID(row) - if err != nil { - return -1, err - } - for i, kr := range rr.keyRanges { - if key.KeyRangeContains(kr, k) { - return i, nil - } - } - return -1, vterrors.Errorf(vtrpc.Code_FAILED_PRECONDITION, "no shard's key range includes the keyspace id: %v for the row: %#v", k, row) -} diff --git a/go/vt/worker/split_clone.go b/go/vt/worker/split_clone.go deleted file mode 100644 index 4ae0b7ca56e..00000000000 --- a/go/vt/worker/split_clone.go +++ /dev/null @@ -1,1411 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package worker - -import ( - "context" - "fmt" - "html/template" - "strings" - "sync" - "time" - - "vitess.io/vitess/go/event" - "vitess.io/vitess/go/stats" - "vitess.io/vitess/go/vt/binlog/binlogplayer" - "vitess.io/vitess/go/vt/concurrency" - "vitess.io/vitess/go/vt/discovery" - "vitess.io/vitess/go/vt/throttler" - "vitess.io/vitess/go/vt/topo" - "vitess.io/vitess/go/vt/topo/topoproto" - "vitess.io/vitess/go/vt/topotools" - "vitess.io/vitess/go/vt/vtctl/schematools" - "vitess.io/vitess/go/vt/vterrors" - "vitess.io/vitess/go/vt/vtgate/vindexes" - "vitess.io/vitess/go/vt/vttablet/tabletconn" - "vitess.io/vitess/go/vt/worker/events" - "vitess.io/vitess/go/vt/wrangler" - - binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" - tabletmanagerdatapb "vitess.io/vitess/go/vt/proto/tabletmanagerdata" - topodatapb "vitess.io/vitess/go/vt/proto/topodata" - "vitess.io/vitess/go/vt/proto/vtrpc" -) - -// cloneType specifies whether it is a horizontal resharding or a vertical split. -// TODO(mberlin): Remove this once we merged both into one command. -type cloneType int - -const ( - horizontalResharding cloneType = iota - verticalSplit -) - -// servingTypes is the list of tabletTypes which the source keyspace must be serving. -var servingTypes = []topodatapb.TabletType{topodatapb.TabletType_PRIMARY, topodatapb.TabletType_REPLICA, topodatapb.TabletType_RDONLY} - -// SplitCloneWorker will clone the data within a keyspace from a -// source set of shards to a destination set of shards. -type SplitCloneWorker struct { - StatusWorker - - wr *wrangler.Wrangler - cloneType cloneType - cell string - destinationKeyspace string - shard string - online bool - offline bool - useConsistentSnapshot bool - // verticalSplit only: List of tables which should be split out. - tables []string - // horizontalResharding only: List of tables which will be skipped. - excludeTables []string - chunkCount int - minRowsPerChunk int - sourceReaderCount int - writeQueryMaxRows int - writeQueryMaxSize int - destinationWriterCount int - minHealthyTablets int - tabletType topodatapb.TabletType - maxTPS int64 - maxReplicationLag int64 - cleaner *wrangler.Cleaner - tabletTracker *TabletTracker - - // populated during WorkerStateInit, read-only after that - destinationKeyspaceInfo *topo.KeyspaceInfo - sourceShards []*topo.ShardInfo - destinationShards []*topo.ShardInfo - keyspaceSchema *vindexes.KeyspaceSchema - // healthCheck is used for the destination shards to a) find out the current - // PRIMARY tablet, b) get the list of healthy RDONLY tablets and c) track the - // replication lag of all REPLICA tablets. - // It must be closed at the end of the command. - healthCheck discovery.LegacyHealthCheck - tsc *discovery.LegacyTabletStatsCache - - // populated during WorkerStateFindTargets, read-only after that - sourceTablets []*topodatapb.Tablet - lastPos string // contains the GTID position for the source - transactions []int64 - - // shardWatchers contains a LegacyTopologyWatcher for each source and destination - // shard. It updates the list of tablets in the healthcheck if replicas are - // added/removed. - // Each watcher must be stopped at the end of the command. - shardWatchers []*discovery.LegacyTopologyWatcher - // destinationDbNames stores for each destination keyspace/shard the MySQL - // database name. - // Example Map Entry: test_keyspace/-80 => vt_test_keyspace - destinationDbNames map[string]string - - // throttlersMu guards the fields within this group. - throttlersMu sync.Mutex - // throttlers has a throttler for each destination shard. - // Map key format: "keyspace/shard" e.g. "test_keyspace/-80" - // Throttlers will be added/removed during WorkerStateClone(Online|Offline). - throttlers map[string]*throttler.Throttler - - // sourceAliases has the list of tablets (per source shard) we took - // offline for the WorkerStateCloneOffline phase. - // Populated shortly before WorkerStateCloneOffline, read-only after that. - sourceAliases []*topodatapb.TabletAlias - - // formattedOfflineSourcesMu guards all fields in this group. - formattedOfflineSourcesMu sync.Mutex - // formattedOfflineSources is a space separated list of - // "sourceAliases". It is used by the StatusAs* methods to output the - // used source tablets during the offline clone phase. - formattedOfflineSources string - - // tableStatusList* holds the status for each table. - // populated during WorkerStateCloneOnline - tableStatusListOnline *tableStatusList - // populated during WorkerStateCloneOffline - tableStatusListOffline *tableStatusList - - ev event.Updater -} - -// newSplitCloneWorker returns a new worker object for the SplitClone command. -func newSplitCloneWorker(wr *wrangler.Wrangler, cell, keyspace, shard string, online, offline bool, excludeTables []string, chunkCount, minRowsPerChunk, sourceReaderCount, writeQueryMaxRows, writeQueryMaxSize, destinationWriterCount, minHealthyTablets int, tabletType topodatapb.TabletType, maxTPS, maxReplicationLag int64, useConsistentSnapshot bool) (Worker, error) { - return newCloneWorker(wr, horizontalResharding, cell, keyspace, shard, online, offline, nil /* tables */, excludeTables, chunkCount, minRowsPerChunk, sourceReaderCount, writeQueryMaxRows, writeQueryMaxSize, destinationWriterCount, minHealthyTablets, tabletType, maxTPS, maxReplicationLag, useConsistentSnapshot) -} - -// newVerticalSplitCloneWorker returns a new worker object for the -// VerticalSplitClone command. -func newVerticalSplitCloneWorker(wr *wrangler.Wrangler, cell, keyspace, shard string, online, offline bool, tables []string, chunkCount, minRowsPerChunk, sourceReaderCount, writeQueryMaxRows, writeQueryMaxSize, destinationWriterCount, minHealthyTablets int, tabletType topodatapb.TabletType, maxTPS, maxReplicationLag int64, useConsistentSnapshot bool) (Worker, error) { - return newCloneWorker(wr, verticalSplit, cell, keyspace, shard, online, offline, tables, nil /* excludeTables */, chunkCount, minRowsPerChunk, sourceReaderCount, writeQueryMaxRows, writeQueryMaxSize, destinationWriterCount, minHealthyTablets, tabletType, maxTPS, maxReplicationLag, useConsistentSnapshot) -} - -// newCloneWorker returns a new SplitCloneWorker object which is used both by -// the SplitClone and VerticalSplitClone command. -// TODO(mberlin): Rename SplitCloneWorker to cloneWorker. -func newCloneWorker(wr *wrangler.Wrangler, cloneType cloneType, cell, keyspace, shard string, online, offline bool, tables, excludeTables []string, chunkCount, minRowsPerChunk, sourceReaderCount, writeQueryMaxRows, writeQueryMaxSize, destinationWriterCount, minHealthyTablets int, tabletType topodatapb.TabletType, maxTPS, maxReplicationLag int64, useConsistentSnapshot bool) (Worker, error) { - if cloneType != horizontalResharding && cloneType != verticalSplit { - return nil, vterrors.Errorf(vtrpc.Code_INTERNAL, "unknown cloneType: %v This is a bug. Please report", cloneType) - } - - // Verify user defined flags. - if !online && !offline { - return nil, vterrors.New(vtrpc.Code_INVALID_ARGUMENT, "at least one clone phase (-online, -offline) must be enabled (and not set to false)") - } - if tables != nil && len(tables) == 0 { - return nil, vterrors.New(vtrpc.Code_INVALID_ARGUMENT, "list of tablets to be split out must not be empty") - } - if chunkCount <= 0 { - return nil, vterrors.Errorf(vtrpc.Code_INVALID_ARGUMENT, "chunk_count must be > 0: %v", chunkCount) - } - if minRowsPerChunk <= 0 { - return nil, vterrors.Errorf(vtrpc.Code_INVALID_ARGUMENT, "min_rows_per_chunk must be > 0: %v", minRowsPerChunk) - } - if sourceReaderCount <= 0 { - return nil, vterrors.Errorf(vtrpc.Code_INVALID_ARGUMENT, "source_reader_count must be > 0: %v", sourceReaderCount) - } - if writeQueryMaxRows <= 0 { - return nil, vterrors.Errorf(vtrpc.Code_INVALID_ARGUMENT, "write_query_max_rows must be > 0: %v", writeQueryMaxRows) - } - if writeQueryMaxSize <= 0 { - return nil, vterrors.Errorf(vtrpc.Code_INVALID_ARGUMENT, "write_query_max_size must be > 0: %v", writeQueryMaxSize) - } - if destinationWriterCount <= 0 { - return nil, vterrors.Errorf(vtrpc.Code_INVALID_ARGUMENT, "destination_writer_count must be > 0: %v", destinationWriterCount) - } - if minHealthyTablets < 0 { - return nil, vterrors.Errorf(vtrpc.Code_INVALID_ARGUMENT, "min_healthy_tablets must be >= 0: %v", minHealthyTablets) - } - if maxTPS != throttler.MaxRateModuleDisabled { - wr.Logger().Infof("throttling enabled and set to a max of %v transactions/second", maxTPS) - } - if maxTPS != throttler.MaxRateModuleDisabled && maxTPS < int64(destinationWriterCount) { - return nil, vterrors.Errorf(vtrpc.Code_INVALID_ARGUMENT, "-max_tps must be >= -destination_writer_count: %v >= %v", maxTPS, destinationWriterCount) - } - if maxReplicationLag <= 0 { - return nil, vterrors.Errorf(vtrpc.Code_INVALID_ARGUMENT, "max_replication_lag must be >= 1s: %v", maxReplicationLag) - } - if tabletType != topodatapb.TabletType_REPLICA && tabletType != topodatapb.TabletType_RDONLY { - return nil, vterrors.Errorf(vtrpc.Code_INVALID_ARGUMENT, "tablet_type must be RDONLY or REPLICA: %v", topodatapb.TabletType_name[int32(tabletType)]) - } - - scw := &SplitCloneWorker{ - StatusWorker: NewStatusWorker(), - wr: wr, - cloneType: cloneType, - cell: cell, - destinationKeyspace: keyspace, - shard: shard, - online: online, - offline: offline, - tables: tables, - excludeTables: excludeTables, - chunkCount: chunkCount, - minRowsPerChunk: minRowsPerChunk, - sourceReaderCount: sourceReaderCount, - writeQueryMaxRows: writeQueryMaxRows, - writeQueryMaxSize: writeQueryMaxSize, - destinationWriterCount: destinationWriterCount, - minHealthyTablets: minHealthyTablets, - maxTPS: maxTPS, - maxReplicationLag: maxReplicationLag, - cleaner: &wrangler.Cleaner{}, - tabletTracker: NewTabletTracker(), - tabletType: tabletType, - tableStatusListOnline: &tableStatusList{}, - tableStatusListOffline: &tableStatusList{}, - useConsistentSnapshot: useConsistentSnapshot, - - throttlers: make(map[string]*throttler.Throttler), - destinationDbNames: make(map[string]string), - } - scw.initializeEventDescriptor() - return scw, nil -} - -func (scw *SplitCloneWorker) initializeEventDescriptor() { - switch scw.cloneType { - case horizontalResharding: - scw.ev = &events.SplitClone{ - Cell: scw.cell, - Keyspace: scw.destinationKeyspace, - Shard: scw.shard, - ExcludeTables: scw.excludeTables, - } - case verticalSplit: - scw.ev = &events.VerticalSplitClone{ - Cell: scw.cell, - Keyspace: scw.destinationKeyspace, - Shard: scw.shard, - Tables: scw.tables, - } - } -} - -func (scw *SplitCloneWorker) setState(state StatusWorkerState) { - scw.SetState(state) - event.DispatchUpdate(scw.ev, state.String()) -} - -func (scw *SplitCloneWorker) setErrorState(err error) { - scw.SetState(WorkerStateError) - event.DispatchUpdate(scw.ev, "error: "+err.Error()) -} - -func (scw *SplitCloneWorker) formatOnlineSources() string { - aliases := scw.tabletTracker.TabletsInUse() - if aliases == "" { - return "no online source tablets currently in use" - } - return aliases -} - -func (scw *SplitCloneWorker) setFormattedOfflineSources(aliases []*topodatapb.TabletAlias) { - scw.formattedOfflineSourcesMu.Lock() - defer scw.formattedOfflineSourcesMu.Unlock() - - var sources []string - for _, alias := range aliases { - sources = append(sources, topoproto.TabletAliasString(alias)) - } - scw.formattedOfflineSources = strings.Join(sources, " ") -} - -// FormattedOfflineSources returns a space separated list of tablets which -// are in use during the offline clone phase. -func (scw *SplitCloneWorker) FormattedOfflineSources() string { - scw.formattedOfflineSourcesMu.Lock() - defer scw.formattedOfflineSourcesMu.Unlock() - - if scw.formattedOfflineSources == "" { - return "no offline source tablets currently in use" - } - return scw.formattedOfflineSources -} - -// StatusAsHTML implements the Worker interface -func (scw *SplitCloneWorker) StatusAsHTML() template.HTML { - state := scw.State() - - result := "Working on: " + scw.destinationKeyspace + "/" + scw.shard + "
    \n" - result += "State: " + state.String() + "
    \n" - switch state { - case WorkerStateCloneOnline: - result += "Running:
    \n" - result += "Copying from: " + scw.formatOnlineSources() + "
    \n" - statuses, eta := scw.tableStatusListOnline.format() - result += "ETA: " + eta.String() + "
    \n" - result += strings.Join(statuses, "
    \n") - case WorkerStateCloneOffline: - result += "Running:
    \n" - result += "Copying from: " + scw.FormattedOfflineSources() + "
    \n" - statuses, eta := scw.tableStatusListOffline.format() - result += "ETA: " + eta.String() + "
    \n" - result += strings.Join(statuses, "
    \n") - if scw.online { - result += "
    \n" - result += "Result from preceding Online Clone:
    \n" - statuses, _ := scw.tableStatusListOnline.format() - result += strings.Join(statuses, "
    \n") - } - case WorkerStateDone: - result += "Success:
    \n" - if scw.online { - result += "
    \n" - result += "Online Clone Result:
    \n" - statuses, _ := scw.tableStatusListOnline.format() - result += strings.Join(statuses, "
    \n") - } - if scw.offline { - result += "
    \n" - result += "Offline Clone Result:
    \n" - statuses, _ := scw.tableStatusListOffline.format() - result += strings.Join(statuses, "
    \n") - } - } - - if (state == WorkerStateCloneOnline || state == WorkerStateCloneOffline) && (scw.maxTPS != throttler.MaxRateModuleDisabled || scw.maxReplicationLag != throttler.ReplicationLagModuleDisabled) { - result += "
    \n" - result += `Resharding Throttler: see /throttlerz for details
    ` - } - - return template.HTML(result) -} - -// StatusAsText implements the Worker interface -func (scw *SplitCloneWorker) StatusAsText() string { - state := scw.State() - - result := "Working on: " + scw.destinationKeyspace + "/" + scw.shard + "\n" - result += "State: " + state.String() + "\n" - switch state { - case WorkerStateCloneOnline: - result += "Running:\n" - result += "Comparing source and destination using: " + scw.formatOnlineSources() + "\n" - statuses, eta := scw.tableStatusListOnline.format() - result += "ETA: " + eta.String() + "\n" - result += strings.Join(statuses, "\n") - case WorkerStateCloneOffline: - result += "Running:\n" - result += "Copying from: " + scw.FormattedOfflineSources() + "\n" - statuses, eta := scw.tableStatusListOffline.format() - result += "ETA: " + eta.String() + "\n" - result += strings.Join(statuses, "\n") - if scw.online { - result += "\n" - result += "\n" - result += "Result from preceding Online Clone:\n" - statuses, _ := scw.tableStatusListOnline.format() - result += strings.Join(statuses, "\n") - } - case WorkerStateDone: - result += "Success:" - if scw.online { - result += "\n" - result += "\n" - result += "Online Clone Result:\n" - statuses, _ := scw.tableStatusListOnline.format() - result += strings.Join(statuses, "\n") - } - if scw.offline { - result += "\n" - result += "\n" - result += "Offline Clone Result:\n" - statuses, _ := scw.tableStatusListOffline.format() - result += strings.Join(statuses, "\n") - } - } - return result -} - -// Run implements the Worker interface -func (scw *SplitCloneWorker) Run(ctx context.Context) error { - resetVars() - - // Run the command. - err := scw.run(ctx) - - // Cleanup. - scw.setState(WorkerStateCleanUp) - // Reverse any changes e.g. setting the tablet type of a source RDONLY tablet. - cerr := scw.cleaner.CleanUp(scw.wr) - if cerr != nil { - if err != nil { - scw.wr.Logger().Errorf2(cerr, "CleanUp failed in addition to job error: %v") - } else { - err = cerr - } - } - - // Stop watchers to prevent new tablets from getting added to the healthCheck. - for _, watcher := range scw.shardWatchers { - watcher.Stop() - } - // Stop healthCheck to make sure it stops calling our listener implementation. - if scw.healthCheck != nil { - // After Close returned, we can be sure that it won't call our listener - // implementation (method StatsUpdate) anymore. - if err := scw.healthCheck.Close(); err != nil { - scw.wr.Logger().Errorf2(err, "LegacyHealthCheck.Close() failed") - } - } - - if err != nil { - scw.setErrorState(err) - return err - } - scw.setState(WorkerStateDone) - return nil -} - -func (scw *SplitCloneWorker) run(ctx context.Context) error { - // Phase 1: read what we need to do. - if err := scw.init(ctx); err != nil { - return vterrors.Wrap(err, "init() failed") - } - if err := checkDone(ctx); err != nil { - return err - } - - // Phase 2: Find destination primary tablets. - if err := scw.findDestinationPrimarys(ctx); err != nil { - return vterrors.Wrap(err, "findDestinationPrimarys() failed") - } - if err := checkDone(ctx); err != nil { - return err - } - - // Phase 3: (optional) online clone. - if scw.online { - scw.wr.Logger().Infof("Online clone will be run now.") - // 3a: Wait for minimum number of source tablets (required for the diff). - if err := scw.waitForTablets(ctx, scw.sourceShards, *waitForHealthyTabletsTimeout); err != nil { - return vterrors.Wrap(err, "waitForTablets(sourceShards) failed") - } - // 3b: Clone the data. - start := time.Now() - if err := scw.clone(ctx, WorkerStateCloneOnline); err != nil { - return vterrors.Wrap(err, "online clone() failed") - } - d := time.Since(start) - if err := checkDone(ctx); err != nil { - return err - } - // TODO(mberlin): Output diff report of the online clone. - // Round duration to second granularity to make it more readable. - scw.wr.Logger().Infof("Online clone finished after %v.", time.Duration(d.Nanoseconds()/time.Second.Nanoseconds()*time.Second.Nanoseconds())) - } else { - scw.wr.Logger().Infof("Online clone skipped because --online=false was specified.") - } - - // Phase 4: offline clone. - if scw.offline { - scw.wr.Logger().Infof("Offline clone will be run now.") - if scw.online { - // Wait until the inserts from the online clone were propagated - // from the destination primary to the rdonly tablets. - // TODO(mberlin): Remove the sleep and get the destination primary position - // instead and wait until all selected destination tablets have reached - // it. - time.Sleep(1 * time.Second) - } - - // 4a: Make sure the sources are producing a stable view of the data - if scw.useConsistentSnapshot { - if err := scw.findTransactionalSources(ctx); err != nil { - return vterrors.Wrap(err, "findSourceTablets() failed") - } - } else { - if err := scw.findOfflineSourceTablets(ctx); err != nil { - return vterrors.Wrap(err, "findSourceTablets() failed") - } - } - if err := checkDone(ctx); err != nil { - return err - } - - // 4b: Clone the data. - start := time.Now() - if err := scw.clone(ctx, WorkerStateCloneOffline); err != nil { - return vterrors.Wrap(err, "offline clone() failed") - } - if err := scw.setUpVReplication(ctx); err != nil { - return vterrors.Wrap(err, "failed to set up replication") - } - - d := time.Since(start) - if err := checkDone(ctx); err != nil { - return err - } - // TODO(mberlin): Output diff report of the offline clone. - // Round duration to second granularity to make it more readable. - scw.wr.Logger().Infof("Offline clone finished after %v.", time.Duration(d.Nanoseconds()/time.Second.Nanoseconds()*time.Second.Nanoseconds())) - } else { - scw.wr.Logger().Infof("Offline clone skipped because --offline=false was specified.") - } - - return nil -} - -// init phase: -// - read the destination keyspace, make sure it has 'servedFrom' values -func (scw *SplitCloneWorker) init(ctx context.Context) error { - scw.setState(WorkerStateInit) - - // read the keyspace and validate it - shortCtx, cancel := context.WithTimeout(ctx, *remoteActionsTimeout) - var err error - scw.destinationKeyspaceInfo, err = scw.wr.TopoServer().GetKeyspace(shortCtx, scw.destinationKeyspace) - cancel() - if err != nil { - return vterrors.Wrapf(err, "cannot read (destination) keyspace %v", scw.destinationKeyspace) - } - - // Set source and destination shard infos. - switch scw.cloneType { - case horizontalResharding: - if err := scw.initShardsForHorizontalResharding(ctx); err != nil { - return vterrors.Wrap(err, "failed initShardsForHorizontalResharding") - } - case verticalSplit: - if err := scw.initShardsForVerticalSplit(ctx); err != nil { - return vterrors.Wrap(err, "failed initShardsForVerticalSplit") - } - } - - if err := scw.sanityCheckShardInfos(ctx); err != nil { - return vterrors.Wrap(err, "failed sanityCheckShardInfos") - } - - if scw.cloneType == horizontalResharding { - if err := scw.loadVSchema(ctx); err != nil { - return vterrors.Wrap(err, "failed loadVSchema") - } - } - - // Initialize healthcheck and add destination shards to it. - scw.healthCheck = discovery.NewLegacyHealthCheck(*healthcheckRetryDelay, *healthCheckTimeout) - scw.tsc = discovery.NewTabletStatsCacheDoNotSetListener(scw.wr.TopoServer(), scw.cell) - // We set sendDownEvents=true because it's required by LegacyTabletStatsCache. - scw.healthCheck.SetListener(scw, true /* sendDownEvents */) - - // Start watchers to get tablets added automatically to healthCheck. - allShards := append(scw.sourceShards, scw.destinationShards...) - for _, si := range allShards { - watcher := discovery.NewLegacyShardReplicationWatcher(ctx, scw.wr.TopoServer(), scw.healthCheck, - scw.cell, si.Keyspace(), si.ShardName(), - *healthCheckTopologyRefresh, discovery.DefaultTopoReadConcurrency) - scw.shardWatchers = append(scw.shardWatchers, watcher) - } - - return nil -} - -func (scw *SplitCloneWorker) initShardsForHorizontalResharding(ctx context.Context) error { - // find the OverlappingShards in the keyspace - shortCtx, cancel := context.WithTimeout(ctx, *remoteActionsTimeout) - osList, err := topotools.FindOverlappingShards(shortCtx, scw.wr.TopoServer(), scw.destinationKeyspace) - cancel() - if err != nil { - return vterrors.Wrapf(err, "cannot FindOverlappingShards in %v", scw.destinationKeyspace) - } - - // find the shard we mentioned in there, if any - os := topotools.OverlappingShardsForShard(osList, scw.shard) - if os == nil { - return vterrors.Errorf(vtrpc.Code_FAILED_PRECONDITION, "the specified shard %v/%v is not in any overlapping shard", scw.destinationKeyspace, scw.shard) - } - scw.wr.Logger().Infof("Found overlapping shards: %+v\n", os) - - // one side should have served types, the other one none, - // figure out which is which, then double check them all - leftServingTypes, err := scw.wr.TopoServer().GetShardServingTypes(ctx, os.Left[0]) - if err != nil { - return fmt.Errorf("cannot get shard serving cells for: %v", os.Left[0]) - } - if len(leftServingTypes) > 0 { - scw.sourceShards = os.Left - scw.destinationShards = os.Right - } else { - scw.sourceShards = os.Right - scw.destinationShards = os.Left - } - - if scw.useConsistentSnapshot && len(scw.sourceShards) > 1 { - return vterrors.Errorf(vtrpc.Code_FAILED_PRECONDITION, "cannot use consistent snapshot against multiple source shards") - } - - return nil -} - -func (scw *SplitCloneWorker) initShardsForVerticalSplit(ctx context.Context) error { - if len(scw.destinationKeyspaceInfo.ServedFroms) == 0 { - return vterrors.Errorf(vtrpc.Code_FAILED_PRECONDITION, "destination keyspace %v has no KeyspaceServedFrom", scw.destinationKeyspace) - } - - // Determine the source keyspace. - servedFrom := "" - for _, st := range servingTypes { - sf := scw.destinationKeyspaceInfo.GetServedFrom(st) - if sf == nil { - return vterrors.Errorf(vtrpc.Code_FAILED_PRECONDITION, "destination keyspace %v is serving type %v", scw.destinationKeyspace, st) - } - if servedFrom == "" { - servedFrom = sf.Keyspace - } else { - if servedFrom != sf.Keyspace { - return vterrors.Errorf(vtrpc.Code_FAILED_PRECONDITION, "destination keyspace %v is serving from multiple source keyspaces %v and %v", scw.destinationKeyspace, servedFrom, sf.Keyspace) - } - } - } - sourceKeyspace := servedFrom - - shortCtx, cancel := context.WithTimeout(ctx, *remoteActionsTimeout) - shardMap, err := scw.wr.TopoServer().FindAllShardsInKeyspace(shortCtx, sourceKeyspace) - cancel() - if err != nil { - return vterrors.Wrapf(err, "cannot find source shard for source keyspace %s", sourceKeyspace) - } - if len(shardMap) != 1 { - return vterrors.Errorf(vtrpc.Code_FAILED_PRECONDITION, "found the wrong number of source shards, there should be only one, %v", shardMap) - } - var sourceShard string - for s := range shardMap { - sourceShard = s - } - - // Init the source and destination shard info. - sourceShardInfo, err := scw.wr.TopoServer().GetShard(ctx, sourceKeyspace, sourceShard) - if err != nil { - return err - } - scw.sourceShards = []*topo.ShardInfo{sourceShardInfo} - destShardInfo, err := scw.wr.TopoServer().GetShard(ctx, scw.destinationKeyspace, scw.shard) - if err != nil { - return err - } - scw.destinationShards = []*topo.ShardInfo{destShardInfo} - - return nil -} - -func (scw *SplitCloneWorker) sanityCheckShardInfos(ctx context.Context) error { - // Verify that filtered replication is not already enabled. - for _, si := range scw.destinationShards { - if len(si.SourceShards) > 0 { - return vterrors.Errorf(vtrpc.Code_FAILED_PRECONDITION, - "destination shard %v/%v has filtered replication already enabled from a previous resharding (ShardInfo is set)."+ - " This requires manual intervention e.g. use vtctl SourceShardDelete to remove it", - si.Keyspace(), si.ShardName()) - } - } - // Verify that the source is serving all serving types. - for _, st := range servingTypes { - for _, si := range scw.sourceShards { - shardServingTypes, err := scw.wr.TopoServer().GetShardServingTypes(ctx, si) - if err != nil { - return fmt.Errorf("failed to get ServingTypes for source shard %v/%v", si.Keyspace(), si.ShardName()) - - } - found := false - for _, shardServingType := range shardServingTypes { - if shardServingType == st { - found = true - } - } - if !found { - return fmt.Errorf("source shard %v/%v is not serving type %v", si.Keyspace(), si.ShardName(), st) - } - } - } - - switch scw.cloneType { - case horizontalResharding: - // Verify that the destination is not serving yet. - for _, si := range scw.destinationShards { - shardServingTypes, err := scw.wr.TopoServer().GetShardServingTypes(ctx, si) - if err != nil { - return fmt.Errorf("failed to get ServingTypes for destination shard %v/%v", si.Keyspace(), si.ShardName()) - - } - if len(shardServingTypes) > 0 { - return fmt.Errorf("destination shard %v/%v is serving some types", si.Keyspace(), si.ShardName()) - } - } - - case verticalSplit: - // Verify that the destination is serving all types. - for _, st := range servingTypes { - for _, si := range scw.destinationShards { - shardServingTypes, err := scw.wr.TopoServer().GetShardServingTypes(ctx, si) - if err != nil { - return fmt.Errorf("failed to get ServingTypes for source shard %v/%v", si.Keyspace(), si.ShardName()) - - } - found := false - for _, shardServingType := range shardServingTypes { - if shardServingType == st { - found = true - } - } - if !found { - return fmt.Errorf("source shard %v/%v is not serving type %v", si.Keyspace(), si.ShardName(), st) - } - } - } - } - - return nil -} - -func (scw *SplitCloneWorker) loadVSchema(ctx context.Context) error { - var keyspaceSchema *vindexes.KeyspaceSchema - kschema, err := scw.wr.TopoServer().GetVSchema(ctx, scw.destinationKeyspace) - if err != nil { - return vterrors.Wrapf(err, "cannot load VSchema for keyspace %v", scw.destinationKeyspace) - } - if kschema == nil { - return vterrors.Errorf(vtrpc.Code_FAILED_PRECONDITION, "no VSchema for keyspace %v", scw.destinationKeyspace) - } - - keyspaceSchema, err = vindexes.BuildKeyspaceSchema(kschema, scw.destinationKeyspace) - if err != nil { - return vterrors.Wrapf(err, "cannot build vschema for keyspace %v", scw.destinationKeyspace) - } - scw.keyspaceSchema = keyspaceSchema - return nil -} - -// findOfflineSourceTablets phase: -// - find one rdonly in the source shard -// - mark it as 'worker' pointing back to us -// - get the aliases of all the source tablets -func (scw *SplitCloneWorker) findOfflineSourceTablets(ctx context.Context) error { - scw.setState(WorkerStateFindTargets) - - // find an appropriate tablet in the source shards - scw.sourceAliases = make([]*topodatapb.TabletAlias, len(scw.sourceShards)) - for i, si := range scw.sourceShards { - var err error - scw.sourceAliases[i], err = FindWorkerTablet(ctx, scw.wr, scw.cleaner, scw.tsc, scw.cell, si.Keyspace(), si.ShardName(), scw.minHealthyTablets, scw.tabletType) - if err != nil { - return vterrors.Wrapf(err, "FindWorkerTablet() failed for %v/%v/%v", scw.cell, si.Keyspace(), si.ShardName()) - } - scw.wr.Logger().Infof("Using tablet %v as source for %v/%v", topoproto.TabletAliasString(scw.sourceAliases[i]), si.Keyspace(), si.ShardName()) - } - scw.setFormattedOfflineSources(scw.sourceAliases) - - // get the tablet info for them, and stop their replication - scw.sourceTablets = make([]*topodatapb.Tablet, len(scw.sourceAliases)) - for i, alias := range scw.sourceAliases { - shortCtx, cancel := context.WithTimeout(ctx, *remoteActionsTimeout) - ti, err := scw.wr.TopoServer().GetTablet(shortCtx, alias) - cancel() - if err != nil { - return vterrors.Wrapf(err, "cannot read tablet %v", topoproto.TabletAliasString(alias)) - } - scw.sourceTablets[i] = ti.Tablet - - shortCtx, cancel = context.WithTimeout(ctx, *remoteActionsTimeout) - err = scw.wr.TabletManagerClient().StopReplication(shortCtx, scw.sourceTablets[i]) - cancel() - if err != nil { - return vterrors.Wrapf(err, "cannot stop replication on tablet %v", topoproto.TabletAliasString(alias)) - } - - wrangler.RecordStartReplicationAction(scw.cleaner, scw.sourceTablets[i]) - } - - return nil -} - -// findTransactionalSources phase: -// - get the aliases of all the source tablets -func (scw *SplitCloneWorker) findTransactionalSources(ctx context.Context) error { - scw.setState(WorkerStateFindTargets) - - if len(scw.sourceShards) > 1 { - return vterrors.Errorf(vtrpc.Code_INVALID_ARGUMENT, "consistent snapshot can only be used with a single source shard") - } - var err error - - // find an appropriate tablet in the source shard - si := scw.sourceShards[0] - scw.sourceAliases = make([]*topodatapb.TabletAlias, 1) - scw.sourceAliases[0], err = FindHealthyTablet(ctx, scw.wr, scw.tsc, scw.cell, si.Keyspace(), si.ShardName(), scw.minHealthyTablets, scw.tabletType) - if err != nil { - return vterrors.Wrapf(err, "FindHealthyTablet() failed for %v/%v/%v", scw.cell, si.Keyspace(), si.ShardName()) - } - scw.wr.Logger().Infof("Using tablet %v as source for %v/%v", topoproto.TabletAliasString(scw.sourceAliases[0]), si.Keyspace(), si.ShardName()) - scw.setFormattedOfflineSources(scw.sourceAliases) - - // get the tablet info - scw.sourceTablets = make([]*topodatapb.Tablet, 1) - shortCtx, cancel := context.WithTimeout(ctx, *remoteActionsTimeout) - ti, err := scw.wr.TopoServer().GetTablet(shortCtx, scw.sourceAliases[0]) - cancel() - if err != nil { - return vterrors.Wrapf(err, "cannot read tablet %v", topoproto.TabletAliasString(scw.sourceAliases[0])) - } - scw.sourceTablets[0] = ti.Tablet - - // stop replication and create transactions to work on - txs, gtid, err := CreateConsistentTransactions(ctx, ti, scw.wr, scw.cleaner, scw.sourceReaderCount) - if err != nil { - return vterrors.Wrapf(err, "error creating consistent transactions") - } - scw.wr.Logger().Infof("created %v transactions", len(txs)) - scw.lastPos = gtid - scw.transactions = txs - return nil -} - -// findDestinationPrimarys finds for each destination shard the current primary. -func (scw *SplitCloneWorker) findDestinationPrimarys(ctx context.Context) error { - scw.setState(WorkerStateFindTargets) - - // Make sure we find a primary for each destination shard and log it. - scw.wr.Logger().Infof("Finding a PRIMARY tablet for each destination shard...") - for _, si := range scw.destinationShards { - waitCtx, waitCancel := context.WithTimeout(ctx, *waitForHealthyTabletsTimeout) - err := scw.tsc.WaitForTablets(waitCtx, si.Keyspace(), si.ShardName(), topodatapb.TabletType_PRIMARY) - waitCancel() - if err != nil { - return vterrors.Wrapf(err, "cannot find PRIMARY tablet for destination shard for %v/%v (in cell: %v)", si.Keyspace(), si.ShardName(), scw.cell) - } - primarys := scw.tsc.GetHealthyTabletStats(si.Keyspace(), si.ShardName(), topodatapb.TabletType_PRIMARY) - if len(primarys) == 0 { - return vterrors.Errorf(vtrpc.Code_FAILED_PRECONDITION, "cannot find PRIMARY tablet for destination shard for %v/%v (in cell: %v) in LegacyHealthCheck: empty LegacyTabletStats list", si.Keyspace(), si.ShardName(), scw.cell) - } - primary := primarys[0] - - // Get the MySQL database name of the tablet. - keyspaceAndShard := topoproto.KeyspaceShardString(si.Keyspace(), si.ShardName()) - scw.destinationDbNames[keyspaceAndShard] = topoproto.TabletDbName(primary.Tablet) - - scw.wr.Logger().Infof("Using tablet %v as destination primary for %v/%v", topoproto.TabletAliasString(primary.Tablet.Alias), si.Keyspace(), si.ShardName()) - } - scw.wr.Logger().Infof("NOTE: The used primary of a destination shard might change over the course of the copy e.g. due to a reparent. The LegacyHealthCheck module will track and log primary changes and any error message will always refer the actually used primary address.") - - return nil -} - -// waitForTablets waits for enough serving tablets in the given -// shard (which can be used as input during the diff). -func (scw *SplitCloneWorker) waitForTablets(ctx context.Context, shardInfos []*topo.ShardInfo, timeout time.Duration) error { - var wg sync.WaitGroup - rec := concurrency.AllErrorRecorder{} - - if scw.minHealthyTablets > 0 && len(shardInfos) > 0 { - scw.wr.Logger().Infof("Waiting %v for %d %s/%s RDONLY tablet(s)", timeout, scw.minHealthyTablets, shardInfos[0].Keyspace(), shardInfos[0].ShardName()) - } - - for _, si := range shardInfos { - wg.Add(1) - go func(keyspace, shard string) { - defer wg.Done() - // We wait for --min_healthy_tablets because we will use several - // tablets per shard to spread reading the chunks of rows across as many - // tablets as possible. - if _, err := waitForHealthyTablets(ctx, scw.wr, scw.tsc, scw.cell, keyspace, shard, scw.minHealthyTablets, timeout, scw.tabletType); err != nil { - rec.RecordError(err) - } - }(si.Keyspace(), si.ShardName()) - } - wg.Wait() - return rec.Error() -} - -func (scw *SplitCloneWorker) findFirstSourceTablet(ctx context.Context, state StatusWorkerState) (*topodatapb.Tablet, error) { - if state == WorkerStateCloneOffline { - // Use the first source tablet which we took offline. - return scw.sourceTablets[0], nil - } - - // Pick any healthy serving source tablet. - si := scw.sourceShards[0] - tablets := scw.tsc.GetHealthyTabletStats(si.Keyspace(), si.ShardName(), scw.tabletType) - if len(tablets) == 0 { - // We fail fast on this problem and don't retry because at the start all tablets should be healthy. - return nil, vterrors.Errorf(vtrpc.Code_FAILED_PRECONDITION, "no healthy %v tablet in source shard (%v) available (required to find out the schema)", topodatapb.TabletType_name[int32(scw.tabletType)], topoproto.KeyspaceShardString(si.Keyspace(), si.ShardName())) - } - return tablets[0].Tablet, nil -} - -func (scw *SplitCloneWorker) getCounters(state StatusWorkerState) ([]*stats.CountersWithSingleLabel, *tableStatusList) { - switch state { - case WorkerStateCloneOnline: - return []*stats.CountersWithSingleLabel{statsOnlineInsertsCounters, statsOnlineUpdatesCounters, statsOnlineDeletesCounters, statsOnlineEqualRowsCounters}, - scw.tableStatusListOnline - case WorkerStateCloneOffline: - return []*stats.CountersWithSingleLabel{statsOfflineInsertsCounters, statsOfflineUpdatesCounters, statsOfflineDeletesCounters, statsOfflineEqualRowsCounters}, - scw.tableStatusListOffline - default: - panic("should not happen") - } -} - -func (scw *SplitCloneWorker) startExecutor(ctx context.Context, wg *sync.WaitGroup, keyspace, shard string, insertChannel chan string, threadID int, processError func(string, ...any)) { - defer wg.Done() - t := scw.getThrottler(keyspace, shard) - //defer t.ThreadFinished(threadID) - - executor := newExecutor(scw.wr, scw.tsc, t, keyspace, shard, threadID) - if err := executor.fetchLoop(ctx, insertChannel); err != nil { - processError("executer.FetchLoop failed: %v", err) - } -} - -func mergeOrSingle(readers []ResultReader, td *tabletmanagerdatapb.TableDefinition) (ResultReader, error) { - if len(readers) == 1 { - return readers[0], nil - } - - sourceReader, err := NewResultMerger(readers, len(td.PrimaryKeyColumns)) - if err != nil { - return nil, err - } - - return sourceReader, nil -} - -func closeReaders(ctx context.Context, readers []ResultReader) { - for _, reader := range readers { - if reader != nil { - reader.Close(ctx) - } - } -} - -func (scw *SplitCloneWorker) getSourceResultReader(ctx context.Context, td *tabletmanagerdatapb.TableDefinition, state StatusWorkerState, chunk chunk, txID int64) (ResultReader, error) { - sourceReaders := make([]ResultReader, len(scw.sourceShards)) - - for shardIndex, si := range scw.sourceShards { - var sourceResultReader ResultReader - if state == WorkerStateCloneOffline && scw.useConsistentSnapshot { - var err error - if txID < 1 { - return nil, vterrors.Errorf(vtrpc.Code_FAILED_PRECONDITION, "tried using consistent snapshot without a valid transaction") - } - tp := newShardTabletProvider(scw.tsc, scw.tabletTracker, si.Keyspace(), si.ShardName(), scw.tabletType) - sourceResultReader, err = NewTransactionalRestartableResultReader(ctx, scw.wr.Logger(), tp, td, chunk, false, txID) - if err != nil { - closeReaders(ctx, sourceReaders) - return nil, vterrors.Wrapf(err, "NewTransactionalRestartableResultReader for source: %v failed", tp.description()) - } - } else { - var err error - var tp tabletProvider - allowMultipleRetries := true - if state == WorkerStateCloneOffline { - tp = newSingleTabletProvider(ctx, scw.wr.TopoServer(), scw.sourceAliases[shardIndex]) - // allowMultipleRetries is false to avoid that we'll keep retrying - // on the same tablet alias for hours. This guards us against the - // situation that an offline tablet gets restarted and serves again. - // In that case we cannot use it because its replication is no - // longer stopped at the same point as we took it offline initially. - allowMultipleRetries = false - } else { - tp = newShardTabletProvider(scw.tsc, scw.tabletTracker, si.Keyspace(), si.ShardName(), scw.tabletType) - } - sourceResultReader, err = NewRestartableResultReader(ctx, scw.wr.Logger(), tp, td, chunk, allowMultipleRetries) - if err != nil { - closeReaders(ctx, sourceReaders) - return nil, vterrors.Wrapf(err, "NewRestartableResultReader for source: %v failed", tp.description()) - } - } - sourceReaders[shardIndex] = sourceResultReader - } - resultReader, err := mergeOrSingle(sourceReaders, td) - if err != nil { - closeReaders(ctx, sourceReaders) - return nil, err - } - return resultReader, err -} - -func (scw *SplitCloneWorker) getDestinationResultReader(ctx context.Context, td *tabletmanagerdatapb.TableDefinition, state StatusWorkerState, chunk chunk) (ResultReader, error) { - destReaders := make([]ResultReader, len(scw.destinationShards)) - - for shardIndex, si := range scw.destinationShards { - tp := newShardTabletProvider(scw.tsc, scw.tabletTracker, si.Keyspace(), si.ShardName(), topodatapb.TabletType_PRIMARY) - destResultReader, err := NewRestartableResultReader(ctx, scw.wr.Logger(), tp, td, chunk, true /* allowMultipleRetries */) - if err != nil { - closeReaders(ctx, destReaders) - return nil, vterrors.Wrapf(err, "NewRestartableResultReader for destination: %v failed", tp.description()) - } - destReaders[shardIndex] = destResultReader - } - resultReader, err := mergeOrSingle(destReaders, td) - if err != nil { - closeReaders(ctx, destReaders) - return nil, err - } - return resultReader, err -} - -func (scw *SplitCloneWorker) cloneAChunk(ctx context.Context, td *tabletmanagerdatapb.TableDefinition, tableIndex int, chunk chunk, processError func(string, ...any), state StatusWorkerState, tableStatusList *tableStatusList, keyResolver keyspaceIDResolver, start time.Time, insertChannels []chan string, txID int64, statsCounters []*stats.CountersWithSingleLabel) { - errPrefix := fmt.Sprintf("table=%v chunk=%v", td.Name, chunk) - - var err error - - if err := checkDone(ctx); err != nil { - processError("%v: Context expired while this thread was waiting for its turn. Context error: %v", errPrefix, err) - return - } - - tableStatusList.threadStarted(tableIndex) - defer tableStatusList.threadDone(tableIndex) - - if state == WorkerStateCloneOnline { - // Wait for enough healthy tablets (they might have become unhealthy - // and their replication lag might have increased since we started.) - if err := scw.waitForTablets(ctx, scw.sourceShards, *retryDuration); err != nil { - processError("%v: No healthy source tablets found (gave up after %v): %v", errPrefix, time.Since(start), err) - return - } - } - - // Set up readers for the diff. There will be one reader for every - // source and destination shard. - sourceReader, err := scw.getSourceResultReader(ctx, td, state, chunk, txID) - if err != nil { - processError("%v NewResultMerger for source tablets failed: %v", errPrefix, err) - return - } - defer sourceReader.Close(ctx) - destReader, err := scw.getDestinationResultReader(ctx, td, state, chunk) - if err != nil { - processError("%v NewResultMerger for destinations tablets failed: %v", errPrefix, err) - return - } - defer destReader.Close(ctx) - dbNames := make([]string, len(scw.destinationShards)) - for i, si := range scw.destinationShards { - keyspaceAndShard := topoproto.KeyspaceShardString(si.Keyspace(), si.ShardName()) - dbNames[i] = scw.destinationDbNames[keyspaceAndShard] - } - // Compare the data and reconcile any differences. - differ, err := NewRowDiffer2(ctx, sourceReader, destReader, td, tableStatusList, tableIndex, - scw.destinationShards, keyResolver, - insertChannels, ctx.Done(), dbNames, scw.writeQueryMaxRows, scw.writeQueryMaxSize, statsCounters) - if err != nil { - processError("%v: NewRowDiffer2 failed: %v", errPrefix, err) - return - } - // Ignore the diff report because all diffs should get reconciled. - _ /* DiffReport */, err = differ.Diff() - if err != nil { - processError("%v: RowDiffer2 failed: %v", errPrefix, err) - return - } -} - -type workUnit struct { - td *tabletmanagerdatapb.TableDefinition - chunk chunk - threadID int - resolver keyspaceIDResolver -} - -func (scw *SplitCloneWorker) startCloningData(ctx context.Context, state StatusWorkerState, sourceSchemaDefinition *tabletmanagerdatapb.SchemaDefinition, - processError func(string, ...any), firstSourceTablet *topodatapb.Tablet, tableStatusList *tableStatusList, - start time.Time, statsCounters []*stats.CountersWithSingleLabel, insertChannels []chan string, wg *sync.WaitGroup) error { - - workPipeline := make(chan workUnit, 10) // We'll use a small buffer so producers do not run too far ahead of consumers - queryService, err := tabletconn.GetDialer()(firstSourceTablet, true) - if err != nil { - return vterrors.Wrap(err, "failed to create queryService") - } - defer queryService.Close(ctx) - - // Let's start the work consumers - for i := 0; i < scw.sourceReaderCount; i++ { - var txID int64 - if scw.useConsistentSnapshot && state == WorkerStateCloneOffline { - txID = scw.transactions[i] - } else { - txID = -1 - } - - wg.Add(1) - go func() { - defer wg.Done() - for work := range workPipeline { - scw.cloneAChunk(ctx, work.td, work.threadID, work.chunk, processError, state, tableStatusList, work.resolver, start, insertChannels, txID, statsCounters) - } - }() - } - - // And now let's start producing work units - for tableIndex, td := range sourceSchemaDefinition.TableDefinitions { - td = reorderColumnsPrimaryKeyFirst(td) - - keyResolver, err := scw.createKeyResolver(td) - if err != nil { - return vterrors.Wrapf(err, "cannot resolve sharding keys for keyspace %v", scw.destinationKeyspace) - } - - // TODO(mberlin): We're going to chunk *all* source shards based on the MIN - // and MAX values of the *first* source shard. Is this going to be a problem? - chunks, err := generateChunks(ctx, scw.wr, firstSourceTablet, td, scw.chunkCount, scw.minRowsPerChunk) - if err != nil { - return vterrors.Wrap(err, "failed to split table into chunks") - } - tableStatusList.setThreadCount(tableIndex, len(chunks)) - - for _, c := range chunks { - workPipeline <- workUnit{td: td, chunk: c, threadID: tableIndex, resolver: keyResolver} - } - } - - close(workPipeline) - - return nil -} - -// copy phase: -// - copy the data from source tablets to destination primaries (with replication on) -// Assumes that the schema has already been created on each destination tablet -// (probably from vtctl's CopySchemaShard) -func (scw *SplitCloneWorker) clone(ctx context.Context, state StatusWorkerState) error { - if state != WorkerStateCloneOnline && state != WorkerStateCloneOffline { - panic(fmt.Sprintf("invalid state passed to clone(): %v", state)) - } - scw.setState(state) - start := time.Now() - defer func() { - statsStateDurationsNs.Set(string(state), time.Since(start).Nanoseconds()) - }() - - var firstSourceTablet, err = scw.findFirstSourceTablet(ctx, state) - if err != nil { - return err - } - - statsCounters, tableStatusList := scw.getCounters(state) - - // The throttlers exist only for the duration of this clone() call. - // That means a SplitClone invocation with both online and offline phases - // will create throttlers for each phase. - if err := scw.createThrottlers(); err != nil { - return err - } - defer scw.closeThrottlers() - - sourceSchemaDefinition, err := scw.getSourceSchema(ctx, firstSourceTablet) - if err != nil { - return err - } - scw.wr.Logger().Infof("Source tablet 0 has %v tables to copy", len(sourceSchemaDefinition.TableDefinitions)) - tableStatusList.initialize(sourceSchemaDefinition) - - var firstError error - - ctx, cancelCopy := context.WithCancel(ctx) - defer cancelCopy() - processError := func(format string, args ...any) { - // in theory we could have two threads see firstError as null and both write to the variable - // that should not cause any problems though - canceling and logging is concurrently safe, - // and overwriting the variable will not cause any problems - scw.wr.Logger().Errorf(format, args...) - if firstError == nil { - firstError = vterrors.Errorf(vtrpc.Code_INTERNAL, format, args...) - cancelCopy() - } - } - - // NOTE: Code below this point must *not* use "return" to exit this Go routine - // early. Instead, "processError" must be called to cancel the context. This - // way all launched Go routines will terminate as well. - // (However, "return" in a new Go routine, i.e. not this one, is fine.) - // If Go routines have already been started, make sure that Wait() on the - // respective WaitGroup is called as well e.g. "destinationWaitGroup.Wait()" - // must always be reached. Waiting for the Go routines is important to avoid - // races between "defer throttler.ThreadFinished()" (must be executed first) - // and "defer scw.closeThrottlers()". Otherwise, vtworker will panic. - - // In parallel, setup the channels to send SQL data chunks to for each destination tablet: - insertChannels := make([]chan string, len(scw.destinationShards)) - destinationWaitGroup := sync.WaitGroup{} - for shardIndex, si := range scw.destinationShards { - // We create one channel per destination tablet. It is sized to have a - // buffer of a maximum of destinationWriterCount * 2 items, to hopefully - // always have data. We then have destinationWriterCount go routines reading - // from it. - insertChannels[shardIndex] = make(chan string, scw.destinationWriterCount*2) - - for j := 0; j < scw.destinationWriterCount; j++ { - destinationWaitGroup.Add(1) - go scw.startExecutor(ctx, &destinationWaitGroup, si.Keyspace(), si.ShardName(), insertChannels[shardIndex], j, processError) - } - } - - // Now for each table, read data chunks and send them to all - // insertChannels - readers := sync.WaitGroup{} - - err = scw.startCloningData(ctx, state, sourceSchemaDefinition, processError, firstSourceTablet, tableStatusList, start, statsCounters, insertChannels, &readers) - if err != nil { - return vterrors.Wrap(err, "failed to startCloningData") - } - readers.Wait() - - for shardIndex := range scw.destinationShards { - close(insertChannels[shardIndex]) - } - destinationWaitGroup.Wait() - - return firstError -} - -func (scw *SplitCloneWorker) setUpVReplication(ctx context.Context) error { - wg := sync.WaitGroup{} - - // get the current position from the sources - sourcePositions := make([]string, len(scw.sourceShards)) - - if scw.useConsistentSnapshot { - sourcePositions[0] = scw.lastPos - } else { - for shardIndex := range scw.sourceShards { - shortCtx, cancel := context.WithTimeout(ctx, *remoteActionsTimeout) - status, err := scw.wr.TabletManagerClient().ReplicationStatus(shortCtx, scw.sourceTablets[shardIndex]) - cancel() - if err != nil { - return err - } - sourcePositions[shardIndex] = status.Position - } - } - cancelableCtx, cancel := context.WithCancel(ctx) - rec := concurrency.AllErrorRecorder{} - handleError := func(e error) { - rec.RecordError(e) - cancel() - } - - for _, si := range scw.destinationShards { - keyspaceAndShard := topoproto.KeyspaceShardString(si.Keyspace(), si.ShardName()) - dbName := scw.destinationDbNames[keyspaceAndShard] - - wg.Add(1) - go func(keyspace, shard string, kr *topodatapb.KeyRange) { - defer wg.Done() - scw.wr.Logger().Infof("Making and populating vreplication table") - - exc := newExecutor(scw.wr, scw.tsc, nil, keyspace, shard, 0) - for shardIndex, src := range scw.sourceShards { - // Check if any error occurred in any other gorouties: - select { - case <-cancelableCtx.Done(): - return // Error somewhere, terminate - default: - } - - bls := &binlogdatapb.BinlogSource{ - Keyspace: src.Keyspace(), - Shard: src.ShardName(), - } - if scw.tables == nil { - bls.KeyRange = kr - } else { - bls.Tables = scw.tables - } - // TODO(mberlin): Fill in scw.maxReplicationLag once the adapative throttler is enabled by default. - qr, err := exc.vreplicationExec(cancelableCtx, binlogplayer.CreateVReplication("SplitClone", bls, sourcePositions[shardIndex], scw.maxTPS, throttler.ReplicationLagModuleDisabled, time.Now().Unix(), dbName)) - if err != nil { - handleError(vterrors.Wrap(err, "vreplication queries failed")) - cancel() - return - } - scw.wr.Logger().Infof("Created replication for tablet %v/%v: %v, db: %v, pos: %v, uid: %v", keyspace, shard, bls, dbName, sourcePositions[shardIndex], uint32(qr.InsertID)) - if err := scw.wr.SourceShardAdd(cancelableCtx, keyspace, shard, uint32(qr.InsertID), src.Keyspace(), src.ShardName(), src.Shard.KeyRange, scw.tables); err != nil { - handleError(vterrors.Wrap(err, "could not add source shard")) - break - } - } - // refreshState will cause the destination to become non-serving because - // it's now participating in the resharding workflow. - if err := exc.refreshState(ctx); err != nil { - handleError(vterrors.Wrapf(err, "RefreshState failed on tablet %v/%v", keyspace, shard)) - } - }(si.Keyspace(), si.ShardName(), si.KeyRange) - } - wg.Wait() - - return rec.Error() -} - -func (scw *SplitCloneWorker) getSourceSchema(ctx context.Context, tablet *topodatapb.Tablet) (*tabletmanagerdatapb.SchemaDefinition, error) { - // get source schema from the first shard - // TODO(alainjobart): for now, we assume the schema is compatible - // on all source shards. Furthermore, we estimate the number of rows - // in each source shard for each table to be about the same - // (rowCount is used to estimate an ETA) - shortCtx, cancel := context.WithTimeout(ctx, *remoteActionsTimeout) - req := &tabletmanagerdatapb.GetSchemaRequest{Tables: scw.tables, ExcludeTables: scw.excludeTables} - sourceSchemaDefinition, err := schematools.GetSchema(shortCtx, scw.wr.TopoServer(), scw.wr.TabletManagerClient(), tablet.Alias, req) - cancel() - if err != nil { - return nil, vterrors.Wrapf(err, "cannot get schema from source %v", topoproto.TabletAliasString(tablet.Alias)) - } - if len(sourceSchemaDefinition.TableDefinitions) == 0 { - return nil, vterrors.Errorf(vtrpc.Code_FAILED_PRECONDITION, "no tables matching the table filter in tablet %v", topoproto.TabletAliasString(tablet.Alias)) - } - for _, td := range sourceSchemaDefinition.TableDefinitions { - if len(td.Columns) == 0 { - return nil, vterrors.Errorf(vtrpc.Code_FAILED_PRECONDITION, "schema for table %v has no columns", td.Name) - } - } - return sourceSchemaDefinition, nil -} - -// createKeyResolver is called at the start of each chunk pipeline. -// It creates a keyspaceIDResolver which translates a given row to a -// keyspace ID. This is necessary to route the to be copied rows to the -// different destination shards. -func (scw *SplitCloneWorker) createKeyResolver(td *tabletmanagerdatapb.TableDefinition) (keyspaceIDResolver, error) { - if scw.cloneType == verticalSplit { - // VerticalSplitClone currently always has exactly one destination shard - // and therefore does not require routing between multiple shards. - return nil, nil - } - return newV3ResolverFromTableDefinition(scw.keyspaceSchema, td) -} - -// StatsUpdate receives replication lag updates for each destination primary -// and forwards them to the respective throttler instance. -// It also forwards any update to the LegacyTabletStatsCache to keep it up to date. -// It is part of the discovery.LegacyHealthCheckStatsListener interface. -func (scw *SplitCloneWorker) StatsUpdate(ts *discovery.LegacyTabletStats) { - scw.tsc.StatsUpdate(ts) - - // Ignore unless REPLICA or RDONLY. - if ts.Target.TabletType != topodatapb.TabletType_REPLICA && ts.Target.TabletType != topodatapb.TabletType_RDONLY { - return - } - - // Lock throttlers mutex to avoid that this method (and the called method - // Throttler.RecordReplicationLag()) races with closeThrottlers() (which calls - // Throttler.Close()). - scw.throttlersMu.Lock() - defer scw.throttlersMu.Unlock() - - t := scw.getThrottlerLocked(ts.Target.Keyspace, ts.Target.Shard) - if t != nil { - t.RecordReplicationLag(time.Now(), ts) - } -} - -func (scw *SplitCloneWorker) createThrottlers() error { - scw.throttlersMu.Lock() - defer scw.throttlersMu.Unlock() - - for _, si := range scw.destinationShards { - // Set up the throttler for each destination shard. - keyspaceAndShard := topoproto.KeyspaceShardString(si.Keyspace(), si.ShardName()) - t, err := throttler.NewThrottler(keyspaceAndShard, "transactions", scw.destinationWriterCount, scw.maxTPS, scw.maxReplicationLag) - if err != nil { - return vterrors.Wrap(err, "cannot instantiate throttler") - } - scw.throttlers[keyspaceAndShard] = t - } - return nil -} - -func (scw *SplitCloneWorker) getThrottler(keyspace, shard string) *throttler.Throttler { - scw.throttlersMu.Lock() - defer scw.throttlersMu.Unlock() - - return scw.getThrottlerLocked(keyspace, shard) -} - -func (scw *SplitCloneWorker) getThrottlerLocked(keyspace, shard string) *throttler.Throttler { - keyspaceAndShard := topoproto.KeyspaceShardString(keyspace, shard) - return scw.throttlers[keyspaceAndShard] -} - -func (scw *SplitCloneWorker) closeThrottlers() { - scw.throttlersMu.Lock() - defer scw.throttlersMu.Unlock() - - for keyspaceAndShard, t := range scw.throttlers { - t.Close() - delete(scw.throttlers, keyspaceAndShard) - } -} diff --git a/go/vt/worker/split_clone_cmd.go b/go/vt/worker/split_clone_cmd.go deleted file mode 100644 index c7a42397402..00000000000 --- a/go/vt/worker/split_clone_cmd.go +++ /dev/null @@ -1,314 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package worker - -import ( - "flag" - "fmt" - "html/template" - "net/http" - "strconv" - "strings" - "sync" - - "context" - - "vitess.io/vitess/go/vt/concurrency" - "vitess.io/vitess/go/vt/proto/topodata" - "vitess.io/vitess/go/vt/proto/vtrpc" - "vitess.io/vitess/go/vt/topo/topoproto" - "vitess.io/vitess/go/vt/topotools" - "vitess.io/vitess/go/vt/vterrors" - "vitess.io/vitess/go/vt/wrangler" -) - -const splitCloneHTML = ` - - - Split Clone Action - - -

    Split Clone Action

    - - {{if .Error}} - Error: {{.Error}}
    - {{else}} -

    Choose the destination keyspace for this action.

    - - {{end}} - -` - -const splitCloneHTML2 = ` - - - Split Clone Action - - -

    Shard involved: {{.Keyspace}}/{{.Shard}}

    -

    Split Clone Action

    -
    - -
    - -
    - -
    - -
    - -
    - -
    - -
    - -
    - -
    - - -
    - -
    - -
    - -
    - - - - ?
    - -
    - -` - -var splitCloneTemplate = mustParseTemplate("splitClone", splitCloneHTML) -var splitCloneTemplate2 = mustParseTemplate("splitClone2", splitCloneHTML2) - -func commandSplitClone(wi *Instance, wr *wrangler.Wrangler, subFlags *flag.FlagSet, args []string) (Worker, error) { - online := subFlags.Bool("online", defaultOnline, "do online copy (optional approximate copy, source and destination tablets will not be put out of serving, minimizes downtime during offline copy)") - offline := subFlags.Bool("offline", defaultOffline, "do offline copy (exact copy at a specific GTID, required before shard migration, source and destination tablets will be put out of serving during copy)") - excludeTables := subFlags.String("exclude_tables", "", "comma separated list of tables to exclude. Each is either an exact match, or a regular expression of the form /regexp/") - chunkCount := subFlags.Int("chunk_count", defaultChunkCount, "number of chunks per table") - minRowsPerChunk := subFlags.Int("min_rows_per_chunk", defaultMinRowsPerChunk, "minimum number of rows per chunk (may reduce --chunk_count)") - sourceReaderCount := subFlags.Int("source_reader_count", defaultSourceReaderCount, "number of concurrent streaming queries to use on the source") - writeQueryMaxRows := subFlags.Int("write_query_max_rows", defaultWriteQueryMaxRows, "maximum number of rows per write query") - writeQueryMaxSize := subFlags.Int("write_query_max_size", defaultWriteQueryMaxSize, "maximum size (in bytes) per write query") - destinationWriterCount := subFlags.Int("destination_writer_count", defaultDestinationWriterCount, "number of concurrent RPCs to execute on the destination") - tabletTypeStr := subFlags.String("tablet_type", "RDONLY", "tablet type to use (RDONLY or REPLICA)") - minHealthyTablets := subFlags.Int("min_healthy_rdonly_tablets", defaultMinHealthyTablets, "minimum number of healthy tablets in the source and destination shard at start") - useConsistentSnapshot := subFlags.Bool("use_consistent_snapshot", defaultUseConsistentSnapshot, "Instead of pausing replication on the source, uses transactions with consistent snapshot to have a stable view of the data.") - maxTPS := subFlags.Int64("max_tps", defaultMaxTPS, "rate limit of maximum number of (write) transactions/second on the destination (unlimited by default)") - maxReplicationLag := subFlags.Int64("max_replication_lag", defaultMaxReplicationLag, "if set, the adapative throttler will be enabled and automatically adjust the write rate to keep the lag below the set value in seconds (disabled by default)") - if err := subFlags.Parse(args); err != nil { - return nil, err - } - if subFlags.NArg() != 1 { - subFlags.Usage() - return nil, vterrors.New(vtrpc.Code_INVALID_ARGUMENT, "command SplitClone requires ") - } - - keyspace, shard, err := topoproto.ParseKeyspaceShard(subFlags.Arg(0)) - if err != nil { - return nil, err - } - var excludeTableArray []string - if *excludeTables != "" { - excludeTableArray = strings.Split(*excludeTables, ",") - } - tabletType, ok := topodata.TabletType_value[*tabletTypeStr] - if !ok { - return nil, vterrors.Errorf(vtrpc.Code_INVALID_ARGUMENT, "command SplitClone invalid tablet_type: %v", tabletType) - } - worker, err := newSplitCloneWorker(wr, wi.cell, keyspace, shard, *online, *offline, excludeTableArray, *chunkCount, *minRowsPerChunk, *sourceReaderCount, *writeQueryMaxRows, *writeQueryMaxSize, *destinationWriterCount, *minHealthyTablets, topodata.TabletType(tabletType), *maxTPS, *maxReplicationLag, *useConsistentSnapshot) - if err != nil { - return nil, vterrors.Wrap(err, "cannot create split clone worker") - } - return worker, nil -} - -func keyspacesWithOverlappingShards(ctx context.Context, wr *wrangler.Wrangler) ([]map[string]string, error) { - shortCtx, cancel := context.WithTimeout(ctx, *remoteActionsTimeout) - keyspaces, err := wr.TopoServer().GetKeyspaces(shortCtx) - cancel() - if err != nil { - return nil, vterrors.Wrap(err, "failed to get list of keyspaces") - } - - wg := sync.WaitGroup{} - mu := sync.Mutex{} // protects result - result := make([]map[string]string, 0, len(keyspaces)) - rec := concurrency.AllErrorRecorder{} - for _, keyspace := range keyspaces { - wg.Add(1) - go func(keyspace string) { - defer wg.Done() - shortCtx, cancel = context.WithTimeout(ctx, *remoteActionsTimeout) - osList, err := topotools.FindOverlappingShards(shortCtx, wr.TopoServer(), keyspace) - cancel() - if err != nil { - rec.RecordError(err) - return - } - mu.Lock() - for _, os := range osList { - result = append(result, map[string]string{ - "Keyspace": os.Left[0].Keyspace(), - "Shard": os.Left[0].ShardName(), - }) - } - mu.Unlock() - }(keyspace) - } - wg.Wait() - - if rec.HasErrors() { - return nil, rec.Error() - } - if len(result) == 0 { - return nil, vterrors.New(vtrpc.Code_FAILED_PRECONDITION, "there are no keyspaces with overlapping shards") - } - return result, nil -} - -func interactiveSplitClone(ctx context.Context, wi *Instance, wr *wrangler.Wrangler, w http.ResponseWriter, r *http.Request) (Worker, *template.Template, map[string]any, error) { - if err := r.ParseForm(); err != nil { - return nil, nil, nil, vterrors.Wrap(err, "cannot parse form") - } - - keyspace := r.FormValue("keyspace") - shard := r.FormValue("shard") - if keyspace == "" || shard == "" { - // display the list of possible splits to choose from - // (just find all the overlapping guys) - result := make(map[string]any) - choices, err := keyspacesWithOverlappingShards(ctx, wr) - if err != nil { - result["Error"] = err.Error() - } else { - result["Choices"] = choices - } - return nil, splitCloneTemplate, result, nil - } - - sourceReaderCountStr := r.FormValue("sourceReaderCount") - if sourceReaderCountStr == "" { - // display the input form - result := make(map[string]any) - result["Keyspace"] = keyspace - result["Shard"] = shard - result["DefaultOnline"] = defaultOnline - result["DefaultOffline"] = defaultOffline - result["DefaultChunkCount"] = fmt.Sprintf("%v", defaultChunkCount) - result["DefaultMinRowsPerChunk"] = fmt.Sprintf("%v", defaultMinRowsPerChunk) - result["DefaultSourceReaderCount"] = fmt.Sprintf("%v", defaultSourceReaderCount) - result["DefaultWriteQueryMaxRows"] = fmt.Sprintf("%v", defaultWriteQueryMaxRows) - result["DefaultWriteQueryMaxSize"] = fmt.Sprintf("%v", defaultWriteQueryMaxSize) - result["DefaultDestinationWriterCount"] = fmt.Sprintf("%v", defaultDestinationWriterCount) - result["DefaultMinHealthyTablets"] = fmt.Sprintf("%v", defaultMinHealthyTablets) - result["DefaultMaxTPS"] = fmt.Sprintf("%v", defaultMaxTPS) - result["DefaultMaxReplicationLag"] = fmt.Sprintf("%v", defaultMaxReplicationLag) - result["DefaultUseConsistentSnapshot"] = fmt.Sprintf("%v", defaultUseConsistentSnapshot) - return nil, splitCloneTemplate2, result, nil - } - - // get other parameters - onlineStr := r.FormValue("online") - online := onlineStr == "true" - offlineStr := r.FormValue("offline") - offline := offlineStr == "true" - excludeTables := r.FormValue("excludeTables") - var excludeTableArray []string - if excludeTables != "" { - excludeTableArray = strings.Split(excludeTables, ",") - } - chunkCountStr := r.FormValue("chunkCount") - chunkCount, err := strconv.ParseInt(chunkCountStr, 0, 64) - if err != nil { - return nil, nil, nil, vterrors.Wrap(err, "cannot parse chunkCount") - } - minRowsPerChunkStr := r.FormValue("minRowsPerChunk") - minRowsPerChunk, err := strconv.ParseInt(minRowsPerChunkStr, 0, 64) - if err != nil { - return nil, nil, nil, vterrors.Wrap(err, "cannot parse minRowsPerChunk") - } - sourceReaderCount, err := strconv.ParseInt(sourceReaderCountStr, 0, 64) - if err != nil { - return nil, nil, nil, vterrors.Wrap(err, "cannot parse sourceReaderCount") - } - writeQueryMaxRowsStr := r.FormValue("writeQueryMaxRows") - writeQueryMaxRows, err := strconv.ParseInt(writeQueryMaxRowsStr, 0, 64) - if err != nil { - return nil, nil, nil, vterrors.Wrap(err, "cannot parse writeQueryMaxRows") - } - writeQueryMaxSizeStr := r.FormValue("writeQueryMaxSize") - writeQueryMaxSize, err := strconv.ParseInt(writeQueryMaxSizeStr, 0, 64) - if err != nil { - return nil, nil, nil, vterrors.Wrap(err, "cannot parse writeQueryMaxSize") - } - destinationWriterCountStr := r.FormValue("destinationWriterCount") - destinationWriterCount, err := strconv.ParseInt(destinationWriterCountStr, 0, 64) - if err != nil { - return nil, nil, nil, vterrors.Wrap(err, "cannot parse destinationWriterCount") - } - minHealthyTabletsStr := r.FormValue("minHealthyTablets") - minHealthyTablets, err := strconv.ParseInt(minHealthyTabletsStr, 0, 64) - if err != nil { - return nil, nil, nil, vterrors.Wrap(err, "cannot parse minHealthyTablets") - } - tabletTypeStr := r.FormValue("tabletType") - tabletType, ok := topodata.TabletType_value[tabletTypeStr] - if !ok { - return nil, nil, nil, vterrors.Errorf(vtrpc.Code_INVALID_ARGUMENT, "command SplitClone invalid tablet_type: %v", tabletType) - } - maxTPSStr := r.FormValue("maxTPS") - maxTPS, err := strconv.ParseInt(maxTPSStr, 0, 64) - if err != nil { - return nil, nil, nil, vterrors.Wrap(err, "cannot parse maxTPS") - } - maxReplicationLagStr := r.FormValue("maxReplicationLag") - maxReplicationLag, err := strconv.ParseInt(maxReplicationLagStr, 0, 64) - if err != nil { - return nil, nil, nil, vterrors.Wrap(err, "cannot parse maxReplicationLag") - } - - useConsistentSnapshotStr := r.FormValue("useConsistentSnapshot") - useConsistentSnapshot := useConsistentSnapshotStr == "true" - - // start the clone job - wrk, err := newSplitCloneWorker(wr, wi.cell, keyspace, shard, online, offline, excludeTableArray, int(chunkCount), int(minRowsPerChunk), int(sourceReaderCount), int(writeQueryMaxRows), int(writeQueryMaxSize), int(destinationWriterCount), int(minHealthyTablets), topodata.TabletType(tabletType), maxTPS, maxReplicationLag, useConsistentSnapshot) - if err != nil { - return nil, nil, nil, vterrors.Wrap(err, "cannot create worker") - } - return wrk, nil, nil, nil -} - -func init() { - AddCommand("Clones", Command{"SplitClone", - commandSplitClone, interactiveSplitClone, - "[--online=false] [--offline=false] [--exclude_tables=''] ", - "Replicates the data and creates configuration for a horizontal split."}) -} diff --git a/go/vt/worker/split_clone_flaky_test.go b/go/vt/worker/split_clone_flaky_test.go deleted file mode 100644 index 95db50ee8a7..00000000000 --- a/go/vt/worker/split_clone_flaky_test.go +++ /dev/null @@ -1,531 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package worker - -import ( - "context" - "errors" - "fmt" - "math" - "strconv" - "strings" - "sync" - "testing" - "time" - - "vitess.io/vitess/go/mysql" - "vitess.io/vitess/go/mysql/fakesqldb" - "vitess.io/vitess/go/sqltypes" - "vitess.io/vitess/go/vt/discovery" - "vitess.io/vitess/go/vt/mysqlctl/tmutils" - "vitess.io/vitess/go/vt/topo" - "vitess.io/vitess/go/vt/topo/memorytopo" - "vitess.io/vitess/go/vt/topo/topoproto" - "vitess.io/vitess/go/vt/topotools" - "vitess.io/vitess/go/vt/vtgate/evalengine" - "vitess.io/vitess/go/vt/vttablet/grpcqueryservice" - "vitess.io/vitess/go/vt/vttablet/queryservice/fakes" - "vitess.io/vitess/go/vt/wrangler/testlib" - - querypb "vitess.io/vitess/go/vt/proto/query" - tabletmanagerdatapb "vitess.io/vitess/go/vt/proto/tabletmanagerdata" - topodatapb "vitess.io/vitess/go/vt/proto/topodata" - vschemapb "vitess.io/vitess/go/vt/proto/vschema" -) - -const ( - // splitCloneTestMin is the minimum value of the primary key. - splitCloneTestMin int = 100 - // splitCloneTestMax is the maximum value of the primary key. - splitCloneTestMax int = 200 - // In the default test case there are 100 rows on the source. - splitCloneTestRowsCount = splitCloneTestMax - splitCloneTestMin -) - -var ( - errReadOnly = errors.New("the MariaDB server is running with the --read-only option so it cannot execute this statement (errno 1290) during query: ") - - errStreamingQueryTimeout = errors.New("vttablet: generic::unknown: error: the query was killed either because it timed out or was canceled: (errno 2013) (sqlstate HY000) during query: ") -) - -type splitCloneTestCase struct { - t *testing.T - - ts *topo.Server - wi *Instance - tablets []*testlib.FakeTablet - - // Source tablets. - // This test uses two source and destination rdonly tablets because - // --min_healthy_rdonly_tablets is set to 2 in the test. - sourceRdonlyQs []*testQueryService - - // Destination tablets. - leftPrimaryFakeDb *fakesqldb.DB - leftPrimaryQs *testQueryService - rightPrimaryFakeDb *fakesqldb.DB - rightPrimaryQs *testQueryService - - // leftReplica is used by the reparent test. - leftReplica *testlib.FakeTablet - leftReplicaFakeDb *fakesqldb.DB - leftReplicaQs *fakes.StreamHealthQueryService - - // leftRdonlyQs are used by the Clone to diff against the source. - leftRdonlyQs []*testQueryService - // rightRdonlyQs are used by the Clone to diff against the source. - rightRdonlyQs []*testQueryService - - // defaultWorkerArgs are the full default arguments to run SplitClone. - defaultWorkerArgs []string - - // Used to restore the default values after the test run - defaultExecuteFetchRetryTime time.Duration - defaultRetryDuration time.Duration -} - -func (tc *splitCloneTestCase) setUp(v3 bool) { - tc.setUpWithConcurrency(v3, 10, 2, splitCloneTestRowsCount) -} - -func (tc *splitCloneTestCase) setUpWithConcurrency(v3 bool, concurrency, writeQueryMaxRows, rowsCount int) { - - // Reset some retry flags for the tests that change that - tc.defaultRetryDuration = *retryDuration - tc.defaultExecuteFetchRetryTime = *executeFetchRetryTime - - tc.ts = memorytopo.NewServer("cell1", "cell2") - ctx := context.Background() - tc.wi = NewInstance(tc.ts, "cell1", time.Second) - - if v3 { - if err := tc.ts.CreateKeyspace(ctx, "ks", &topodatapb.Keyspace{}); err != nil { - tc.t.Fatalf("CreateKeyspace v3 failed: %v", err) - } - - vs := &vschemapb.Keyspace{ - Sharded: true, - Vindexes: map[string]*vschemapb.Vindex{ - "table1_index": { - Type: "numeric", - }, - }, - Tables: map[string]*vschemapb.Table{ - "table1": { - ColumnVindexes: []*vschemapb.ColumnVindex{ - { - Column: "keyspace_id", - Name: "table1_index", - }, - }, - }, - }, - } - if err := tc.ts.SaveVSchema(ctx, "ks", vs); err != nil { - tc.t.Fatalf("SaveVSchema v3 failed: %v", err) - } - } else { - if err := tc.ts.CreateKeyspace(ctx, "ks", &topodatapb.Keyspace{}); err != nil { - tc.t.Fatalf("CreateKeyspace v2 failed: %v", err) - } - } - - // Create the fake databases. - sourceRdonlyFakeDB := sourceRdonlyFakeDB(tc.t, "vt_ks", "table1", splitCloneTestMin, splitCloneTestMax) - tc.leftPrimaryFakeDb = fakesqldb.New(tc.t).SetName("leftPrimary").OrderMatters() - tc.leftReplicaFakeDb = fakesqldb.New(tc.t).SetName("leftReplica").OrderMatters() - tc.rightPrimaryFakeDb = fakesqldb.New(tc.t).SetName("rightPrimary").OrderMatters() - - sourcePrimary := testlib.NewFakeTablet(tc.t, tc.wi.wr, "cell1", 0, - topodatapb.TabletType_PRIMARY, nil, testlib.TabletKeyspaceShard(tc.t, "ks", "-80")) - sourceRdonly1 := testlib.NewFakeTablet(tc.t, tc.wi.wr, "cell1", 1, - topodatapb.TabletType_RDONLY, sourceRdonlyFakeDB, testlib.TabletKeyspaceShard(tc.t, "ks", "-80")) - sourceRdonly2 := testlib.NewFakeTablet(tc.t, tc.wi.wr, "cell1", 2, - topodatapb.TabletType_RDONLY, sourceRdonlyFakeDB, testlib.TabletKeyspaceShard(tc.t, "ks", "-80")) - - leftPrimary := testlib.NewFakeTablet(tc.t, tc.wi.wr, "cell1", 10, - topodatapb.TabletType_PRIMARY, tc.leftPrimaryFakeDb, testlib.TabletKeyspaceShard(tc.t, "ks", "-40")) - // leftReplica is used by the reparent test. - leftReplica := testlib.NewFakeTablet(tc.t, tc.wi.wr, "cell1", 11, - topodatapb.TabletType_REPLICA, tc.leftReplicaFakeDb, testlib.TabletKeyspaceShard(tc.t, "ks", "-40")) - tc.leftReplica = leftReplica - leftRdonly1 := testlib.NewFakeTablet(tc.t, tc.wi.wr, "cell1", 12, - topodatapb.TabletType_RDONLY, nil, testlib.TabletKeyspaceShard(tc.t, "ks", "-40")) - leftRdonly2 := testlib.NewFakeTablet(tc.t, tc.wi.wr, "cell1", 13, - topodatapb.TabletType_RDONLY, nil, testlib.TabletKeyspaceShard(tc.t, "ks", "-40")) - - rightPrimary := testlib.NewFakeTablet(tc.t, tc.wi.wr, "cell1", 20, - topodatapb.TabletType_PRIMARY, tc.rightPrimaryFakeDb, testlib.TabletKeyspaceShard(tc.t, "ks", "40-80")) - rightRdonly1 := testlib.NewFakeTablet(tc.t, tc.wi.wr, "cell1", 22, - topodatapb.TabletType_RDONLY, nil, testlib.TabletKeyspaceShard(tc.t, "ks", "40-80")) - rightRdonly2 := testlib.NewFakeTablet(tc.t, tc.wi.wr, "cell1", 23, - topodatapb.TabletType_RDONLY, nil, testlib.TabletKeyspaceShard(tc.t, "ks", "40-80")) - - tc.tablets = []*testlib.FakeTablet{sourcePrimary, sourceRdonly1, sourceRdonly2, - leftPrimary, tc.leftReplica, leftRdonly1, leftRdonly2, rightPrimary, rightRdonly1, rightRdonly2} - - // add the topo and schema data we'll need - if err := tc.ts.CreateShard(ctx, "ks", "80-"); err != nil { - tc.t.Fatalf("CreateShard(\"-80\") failed: %v", err) - } - if err := topotools.RebuildKeyspace(ctx, tc.wi.wr.Logger(), tc.wi.wr.TopoServer(), "ks", nil, false); err != nil { - tc.t.Fatalf("RebuildKeyspaceGraph failed: %v", err) - } - - for _, sourceRdonly := range []*testlib.FakeTablet{sourceRdonly1, sourceRdonly2} { - sourceRdonly.FakeMysqlDaemon.Schema = &tabletmanagerdatapb.SchemaDefinition{ - DatabaseSchema: "", - TableDefinitions: []*tabletmanagerdatapb.TableDefinition{ - { - Name: "table1", - // "id" is the last column in the list on purpose to test for - // regressions. The reconciliation code will SELECT with the primary - // key columns first. The same ordering must be used throughout the - // process e.g. by RowAggregator or the v2Resolver. - Columns: []string{"msg", "keyspace_id", "id"}, - PrimaryKeyColumns: []string{"id"}, - Type: tmutils.TableBaseTable, - // Set the row count to avoid that --min_rows_per_chunk reduces the - // number of chunks. - RowCount: uint64(rowsCount), - }, - }, - } - sourceRdonly.FakeMysqlDaemon.CurrentPrimaryPosition = mysql.Position{ - GTIDSet: mysql.MariadbGTIDSet{12: mysql.MariadbGTID{Domain: 12, Server: 34, Sequence: 5678}}, - } - sourceRdonly.FakeMysqlDaemon.ExpectedExecuteSuperQueryList = []string{ - "STOP SLAVE", - "START SLAVE", - } - shqs := fakes.NewStreamHealthQueryService(sourceRdonly.Target()) - shqs.AddDefaultHealthResponse() - qs := newTestQueryService(tc.t, sourceRdonly.Target(), shqs, 0, 1, topoproto.TabletAliasString(sourceRdonly.Tablet.Alias), false /* omitKeyspaceID */) - qs.addGeneratedRows(100, 100+rowsCount) - grpcqueryservice.Register(sourceRdonly.RPCServer, qs) - tc.sourceRdonlyQs = append(tc.sourceRdonlyQs, qs) - } - // Set up destination rdonlys which will be used as input for the diff during the clone. - for i, destRdonly := range []*testlib.FakeTablet{leftRdonly1, rightRdonly1, leftRdonly2, rightRdonly2} { - shqs := fakes.NewStreamHealthQueryService(destRdonly.Target()) - shqs.AddDefaultHealthResponse() - qs := newTestQueryService(tc.t, destRdonly.Target(), shqs, i%2, 2, topoproto.TabletAliasString(destRdonly.Tablet.Alias), false /* omitKeyspaceID */) - grpcqueryservice.Register(destRdonly.RPCServer, qs) - if i%2 == 0 { - tc.leftRdonlyQs = append(tc.leftRdonlyQs, qs) - } else { - tc.rightRdonlyQs = append(tc.rightRdonlyQs, qs) - } - } - - // In the default test case there will be 30 inserts per destination shard - // because 10 writer threads will insert 5 rows on each destination shard. - // (100 rowsCount / 10 writers / 2 shards = 5 rows.) - // Due to --write_query_max_rows=2 there will be 3 inserts for 5 rows. - rowsPerDestinationShard := rowsCount / 2 - rowsPerThread := rowsPerDestinationShard / concurrency - insertsPerThread := math.Ceil(float64(rowsPerThread) / float64(writeQueryMaxRows)) - insertsTotal := int(insertsPerThread) * concurrency - for i := 1; i <= insertsTotal; i++ { - tc.leftPrimaryFakeDb.AddExpectedQuery("INSERT INTO `vt_ks`.`table1` (`id`, `msg`, `keyspace_id`) VALUES (*", nil) - // leftReplica is unused by default. - tc.rightPrimaryFakeDb.AddExpectedQuery("INSERT INTO `vt_ks`.`table1` (`id`, `msg`, `keyspace_id`) VALUES (*", nil) - } - - // Fake stream health responses because vtworker needs them to find the primary. - shqs := fakes.NewStreamHealthQueryService(leftPrimary.Target()) - shqs.AddDefaultHealthResponse() - tc.leftPrimaryQs = newTestQueryService(tc.t, leftPrimary.Target(), shqs, 0, 2, topoproto.TabletAliasString(leftPrimary.Tablet.Alias), false /* omitKeyspaceID */) - tc.leftReplicaQs = fakes.NewStreamHealthQueryService(leftReplica.Target()) - shqs = fakes.NewStreamHealthQueryService(rightPrimary.Target()) - shqs.AddDefaultHealthResponse() - tc.rightPrimaryQs = newTestQueryService(tc.t, rightPrimary.Target(), shqs, 1, 2, topoproto.TabletAliasString(rightPrimary.Tablet.Alias), false /* omitKeyspaceID */) - grpcqueryservice.Register(leftPrimary.RPCServer, tc.leftPrimaryQs) - grpcqueryservice.Register(leftReplica.RPCServer, tc.leftReplicaQs) - grpcqueryservice.Register(rightPrimary.RPCServer, tc.rightPrimaryQs) - - tc.defaultWorkerArgs = []string{ - "SplitClone", - "-online=false", - // --max_tps is only specified to enable the throttler and ensure that the - // code is executed. But the intent here is not to throttle the test, hence - // the rate limit is set very high. - "-max_tps", "9999", - "-write_query_max_rows", strconv.Itoa(writeQueryMaxRows), - "-chunk_count", strconv.Itoa(concurrency), - "-min_rows_per_chunk", strconv.Itoa(rowsPerThread), - "-source_reader_count", strconv.Itoa(concurrency), - "-destination_writer_count", strconv.Itoa(concurrency), - "ks/-80"} - - // Start action loop after having registered all RPC services. - for _, ft := range tc.tablets { - ft.StartActionLoop(tc.t, tc.wi.wr) - } -} - -func (tc *splitCloneTestCase) tearDown() { - *retryDuration = tc.defaultRetryDuration - *executeFetchRetryTime = tc.defaultExecuteFetchRetryTime - - for _, ft := range tc.tablets { - ft.StopActionLoop(tc.t) - ft.RPCServer.Stop() - ft.FakeMysqlDaemon.Close() - ft.TM = nil - ft.RPCServer = nil - ft.FakeMysqlDaemon = nil - } - tc.leftPrimaryFakeDb.VerifyAllExecutedOrFail() - tc.leftReplicaFakeDb.VerifyAllExecutedOrFail() - tc.rightPrimaryFakeDb.VerifyAllExecutedOrFail() -} - -// testQueryService is a local QueryService implementation to support the tests. -type testQueryService struct { - t *testing.T - - // target is used in the log output. - target *querypb.Target - *fakes.StreamHealthQueryService - shardIndex int - shardCount int - alias string - // omitKeyspaceID is true when the returned rows should not contain the - // "keyspace_id" column. - omitKeyspaceID bool - fields []*querypb.Field - rows [][]sqltypes.Value - - // mu guards the fields in this group. - // It is necessary because multiple Go routines will read from the same - // tablet. - mu sync.Mutex - // forceError is set to true for a given int64 primary key value if - // testQueryService should return an error instead of the actual row. - forceError map[int64]int - // errorCallback is run once after the first error is returned. - errorCallback func() -} - -func newTestQueryService(t *testing.T, target *querypb.Target, shqs *fakes.StreamHealthQueryService, shardIndex, shardCount int, alias string, omitKeyspaceID bool) *testQueryService { - fields := v2Fields - if omitKeyspaceID { - fields = v3Fields - } - return &testQueryService{ - t: t, - target: target, - shardIndex: shardIndex, - shardCount: shardCount, - alias: alias, - omitKeyspaceID: omitKeyspaceID, - fields: fields, - forceError: make(map[int64]int), - - StreamHealthQueryService: shqs, - } -} - -func (sq *testQueryService) StreamExecute(ctx context.Context, target *querypb.Target, sql string, bindVariables map[string]*querypb.BindVariable, transactionID int64, reservedID int64, options *querypb.ExecuteOptions, callback func(*sqltypes.Result) error) error { - // Custom parsing of the query we expect. - // Example: SELECT `id`, `msg`, `keyspace_id` FROM table1 WHERE id>=180 AND id<190 ORDER BY id - min := math.MinInt32 - max := math.MaxInt32 - var err error - parts := strings.Split(sql, " ") - for _, part := range parts { - if strings.HasPrefix(part, "`id`>=") { - // Chunk start. - min, err = strconv.Atoi(part[6:]) - if err != nil { - return err - } - } else if strings.HasPrefix(part, "`id`>") { - // Chunk start after restart. - min, err = strconv.Atoi(part[5:]) - if err != nil { - return err - } - // Increment by one to fulfill ">" instead of ">=". - min++ - } else if strings.HasPrefix(part, "`id`<") { - // Chunk end. - max, err = strconv.Atoi(part[5:]) - if err != nil { - return err - } - } - } - sq.t.Logf("testQueryService: %v,%v/%v/%v: got query: %v with min %v max (exclusive) %v", sq.alias, sq.target.Keyspace, sq.target.Shard, sq.target.TabletType, sql, min, max) - - if sq.forceErrorOnce(int64(min)) { - sq.t.Logf("testQueryService: %v,%v/%v/%v: sending error for id: %v before sending the fields", sq.alias, sq.target.Keyspace, sq.target.Shard, sq.target.TabletType, min) - return errStreamingQueryTimeout - } - - // Send the headers. - if err := callback(&sqltypes.Result{Fields: sq.fields}); err != nil { - return err - } - - // Send the values. - rowsAffected := 0 - for _, row := range sq.rows { - v, _ := evalengine.ToNative(row[0]) - primaryKey := v.(int64) - - if primaryKey >= int64(min) && primaryKey < int64(max) { - if sq.forceErrorOnce(primaryKey) { - sq.t.Logf("testQueryService: %v,%v/%v/%v: sending error for id: %v row: %v", sq.alias, sq.target.Keyspace, sq.target.Shard, sq.target.TabletType, primaryKey, row) - return errStreamingQueryTimeout - } - - if err := callback(&sqltypes.Result{ - Rows: [][]sqltypes.Value{row}, - }); err != nil { - return err - } - // Uncomment the next line during debugging when needed. - // sq.t.Logf("testQueryService: %v,%v/%v/%v: sent row for id: %v row: %v", sq.alias, sq.target.Keyspace, sq.target.Shard, sq.target.TabletType, primaryKey, row) - rowsAffected++ - } - } - - if rowsAffected == 0 { - sq.t.Logf("testQueryService: %v,%v/%v/%v: no rows were sent (%v are available)", sq.alias, sq.target.Keyspace, sq.target.Shard, sq.target.TabletType, len(sq.rows)) - } - return nil -} - -// addGeneratedRows will add from-to generated rows. The rows (their primary -// key) will be in the range [from, to). -func (sq *testQueryService) addGeneratedRows(from, to int) { - var rows [][]sqltypes.Value - // ksids has keyspace ids which are covered by the shard key ranges -40 and 40-80. - ksids := []uint64{0x2000000000000000, 0x6000000000000000} - - for id := from; id < to; id++ { - // Only return the rows which are covered by this shard. - shardIndex := id % 2 - if sq.shardCount == 1 || shardIndex == sq.shardIndex { - idValue := sqltypes.NewInt64(int64(id)) - - row := []sqltypes.Value{ - idValue, - sqltypes.NewVarBinary(fmt.Sprintf("Text for %v", id)), - } - if !sq.omitKeyspaceID { - row = append(row, sqltypes.NewVarBinary(fmt.Sprintf("%v", ksids[shardIndex]))) - } - rows = append(rows, row) - } - } - - if sq.rows == nil { - sq.rows = rows - } else { - sq.rows = append(sq.rows, rows...) - } -} - -func (sq *testQueryService) modifyFirstRows(count int) { - // Modify the text of the first "count" rows. - for i := 0; i < count; i++ { - row := sq.rows[i] - row[1] = sqltypes.NewVarBinary(fmt.Sprintf("OUTDATED ROW: %v", row[1])) - } -} - -func (sq *testQueryService) clearRows() { - sq.rows = nil -} - -func (sq *testQueryService) errorStreamAtRow(primaryKey, times int) { - sq.mu.Lock() - defer sq.mu.Unlock() - - sq.forceError[int64(primaryKey)] = times -} - -// setErrorCallback registers a function which will be called when the first -// error is injected. It will be run only once. -func (sq *testQueryService) setErrorCallback(cb func()) { - sq.mu.Lock() - defer sq.mu.Unlock() - - sq.errorCallback = cb -} - -func (sq *testQueryService) forceErrorOnce(primaryKey int64) bool { - sq.mu.Lock() - defer sq.mu.Unlock() - - force := sq.forceError[primaryKey] > 0 - if force { - sq.forceError[primaryKey]-- - if sq.errorCallback != nil { - sq.errorCallback() - sq.errorCallback = nil - } - } - return force -} - -var v2Fields = []*querypb.Field{ - { - Name: "id", - Type: sqltypes.Int64, - }, - { - Name: "msg", - Type: sqltypes.VarChar, - }, - { - Name: "keyspace_id", - Type: sqltypes.Int64, - }, -} - -// v3Fields is identical to v2Fields but lacks the "keyspace_id" column. -var v3Fields = []*querypb.Field{ - { - Name: "id", - Type: sqltypes.Int64, - }, - { - Name: "msg", - Type: sqltypes.VarChar, - }, -} - -func TestSplitCloneV3(t *testing.T) { - delay := discovery.GetTabletPickerRetryDelay() - defer func() { - discovery.SetTabletPickerRetryDelay(delay) - }() - discovery.SetTabletPickerRetryDelay(5 * time.Millisecond) - - tc := &splitCloneTestCase{t: t} - tc.setUp(true /* v3 */) - defer tc.tearDown() - - // Run the vtworker command. - if err := runCommand(t, tc.wi, tc.wi.wr, tc.defaultWorkerArgs); err != nil { - t.Fatal(err) - } -} diff --git a/go/vt/worker/split_diff.go b/go/vt/worker/split_diff.go deleted file mode 100644 index 415f68a93ab..00000000000 --- a/go/vt/worker/split_diff.go +++ /dev/null @@ -1,580 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package worker - -import ( - "context" - "html/template" - "sort" - "sync" - - "vitess.io/vitess/go/sqltypes" - "vitess.io/vitess/go/sync2" - "vitess.io/vitess/go/vt/binlog/binlogplayer" - "vitess.io/vitess/go/vt/concurrency" - "vitess.io/vitess/go/vt/key" - "vitess.io/vitess/go/vt/mysqlctl/tmutils" - "vitess.io/vitess/go/vt/topo" - "vitess.io/vitess/go/vt/vtctl/schematools" - "vitess.io/vitess/go/vt/vterrors" - "vitess.io/vitess/go/vt/vtgate/vindexes" - "vitess.io/vitess/go/vt/wrangler" - - tabletmanagerdatapb "vitess.io/vitess/go/vt/proto/tabletmanagerdata" - topodatapb "vitess.io/vitess/go/vt/proto/topodata" - "vitess.io/vitess/go/vt/proto/vtrpc" -) - -// SplitDiffWorker executes a diff between a destination shard and its -// source shards in a shard split case. -type SplitDiffWorker struct { - StatusWorker - - wr *wrangler.Wrangler - cell string - keyspace string - shard string - sourceUID uint32 - sourceShard *topodatapb.Shard_SourceShard - excludeTables []string - minHealthyRdonlyTablets int - destinationTabletType topodatapb.TabletType - parallelDiffsCount int - skipVerify bool - cleaner *wrangler.Cleaner - - // populated during WorkerStateInit, read-only after that - keyspaceInfo *topo.KeyspaceInfo - shardInfo *topo.ShardInfo - - // populated during WorkerStateFindTargets, read-only after that - sourceAlias *topodatapb.TabletAlias - destinationAlias *topodatapb.TabletAlias - - // populated during WorkerStateDiff - sourceSchemaDefinition *tabletmanagerdatapb.SchemaDefinition - destinationSchemaDefinition *tabletmanagerdatapb.SchemaDefinition -} - -// NewSplitDiffWorker returns a new SplitDiffWorker object. -func NewSplitDiffWorker(wr *wrangler.Wrangler, cell, keyspace, shard string, sourceUID uint32, excludeTables []string, minHealthyRdonlyTablets, parallelDiffsCount int, tabletType topodatapb.TabletType, skipVerify bool) Worker { - return &SplitDiffWorker{ - StatusWorker: NewStatusWorker(), - wr: wr, - cell: cell, - keyspace: keyspace, - shard: shard, - sourceUID: sourceUID, - excludeTables: excludeTables, - minHealthyRdonlyTablets: minHealthyRdonlyTablets, - destinationTabletType: tabletType, - parallelDiffsCount: parallelDiffsCount, - skipVerify: skipVerify, - cleaner: &wrangler.Cleaner{}, - } -} - -// StatusAsHTML is part of the Worker interface -func (sdw *SplitDiffWorker) StatusAsHTML() template.HTML { - state := sdw.State() - - result := "Working on: " + sdw.keyspace + "/" + sdw.shard + "
    \n" - result += "State: " + state.String() + "
    \n" - switch state { - case WorkerStateDiff: - result += "Running...
    \n" - case WorkerStateDiffWillFail: - result += "Running - have already found differences...
    \n" - case WorkerStateDone: - result += "Success.
    \n" - } - - return template.HTML(result) -} - -// StatusAsText is part of the Worker interface -func (sdw *SplitDiffWorker) StatusAsText() string { - state := sdw.State() - - result := "Working on: " + sdw.keyspace + "/" + sdw.shard + "\n" - result += "State: " + state.String() + "\n" - switch state { - case WorkerStateDiff: - result += "Running...\n" - case WorkerStateDiffWillFail: - result += "Running - have already found differences...\n" - case WorkerStateDone: - result += "Success.\n" - } - return result -} - -// Run is mostly a wrapper to run the cleanup at the end. -func (sdw *SplitDiffWorker) Run(ctx context.Context) error { - resetVars() - err := sdw.run(ctx) - - sdw.SetState(WorkerStateCleanUp) - cerr := sdw.cleaner.CleanUp(sdw.wr) - if cerr != nil { - if err != nil { - sdw.wr.Logger().Errorf2(cerr, "CleanUp failed in addition to job error") - } else { - err = cerr - } - } - if err != nil { - sdw.wr.Logger().Errorf2(err, "Run() error") - sdw.SetState(WorkerStateError) - return err - } - sdw.SetState(WorkerStateDone) - return nil -} - -func (sdw *SplitDiffWorker) run(ctx context.Context) error { - // first state: read what we need to do - if err := sdw.init(ctx); err != nil { - return vterrors.Wrap(err, "init() failed") - } - if err := checkDone(ctx); err != nil { - return err - } - - // second state: find targets - if err := sdw.findTargets(ctx); err != nil { - return vterrors.Wrap(err, "findTargets() failed") - } - if err := checkDone(ctx); err != nil { - return err - } - - // third phase: synchronize replication - if err := sdw.synchronizeReplication(ctx); err != nil { - return vterrors.Wrap(err, "synchronizeReplication() failed") - } - if err := checkDone(ctx); err != nil { - return err - } - - // fourth phase: diff - if err := sdw.diff(ctx); err != nil { - return vterrors.Wrap(err, "diff() failed") - } - if err := checkDone(ctx); err != nil { - return err - } - - return nil -} - -// init phase: -// - read the shard info, make sure it has sources -func (sdw *SplitDiffWorker) init(ctx context.Context) error { - sdw.SetState(WorkerStateInit) - - var err error - shortCtx, cancel := context.WithTimeout(ctx, *remoteActionsTimeout) - sdw.keyspaceInfo, err = sdw.wr.TopoServer().GetKeyspace(shortCtx, sdw.keyspace) - cancel() - if err != nil { - return vterrors.Wrapf(err, "cannot read keyspace %v", sdw.keyspace) - } - shortCtx, cancel = context.WithTimeout(ctx, *remoteActionsTimeout) - sdw.shardInfo, err = sdw.wr.TopoServer().GetShard(shortCtx, sdw.keyspace, sdw.shard) - cancel() - if err != nil { - return vterrors.Wrapf(err, "cannot read shard %v/%v", sdw.keyspace, sdw.shard) - } - - if len(sdw.shardInfo.SourceShards) == 0 { - return vterrors.Errorf(vtrpc.Code_FAILED_PRECONDITION, "shard %v/%v has no source shard", sdw.keyspace, sdw.shard) - } - if sdw.sourceUID == 0 { - if len(sdw.shardInfo.SourceShards) == 1 { - sdw.sourceShard = sdw.shardInfo.SourceShards[0] - } else { - return vterrors.Errorf(vtrpc.Code_FAILED_PRECONDITION, "shard %v/%v has more than one source, please specify a source UID", sdw.keyspace, sdw.shard) - } - } else { - for _, ss := range sdw.shardInfo.SourceShards { - if ss.Uid == sdw.sourceUID { - sdw.sourceShard = ss - break - } - } - } - if sdw.sourceShard == nil { - return vterrors.Errorf(vtrpc.Code_FAILED_PRECONDITION, "shard %v/%v has no source shard with UID %v", sdw.keyspace, sdw.shard, sdw.sourceUID) - } - - if !sdw.shardInfo.HasPrimary() { - return vterrors.Errorf(vtrpc.Code_FAILED_PRECONDITION, "shard %v/%v has no primary", sdw.keyspace, sdw.shard) - } - - return nil -} - -// findTargets phase: -// - find one rdonly per source shard -// - find one rdonly in destination shard -// - mark them all as 'worker' pointing back to us -func (sdw *SplitDiffWorker) findTargets(ctx context.Context) error { - sdw.SetState(WorkerStateFindTargets) - - // find an appropriate tablet in destination shard - var err error - sdw.destinationAlias, err = FindWorkerTablet( - ctx, - sdw.wr, - sdw.cleaner, - nil, /* tsc */ - sdw.cell, - sdw.keyspace, - sdw.shard, - 1, /* minHealthyTablets */ - sdw.destinationTabletType, - ) - if err != nil { - return vterrors.Wrapf(err, "FindWorkerTablet() failed for %v/%v/%v", sdw.cell, sdw.keyspace, sdw.shard) - } - - // find an appropriate tablet in the source shard - // During an horizontal shard split, multiple workers will race to get - // a RDONLY tablet in the source shard. When this happen, concurrent calls - // to FindWorkerTablet could attempt to set to DRAIN state the same tablet. Only - // one of these calls to FindWorkerTablet will succeed and the rest will fail. - // The following, makes sures we keep trying to find a worker tablet when this error occur. - shortCtx, cancel := context.WithTimeout(ctx, *remoteActionsTimeout) - for { - select { - case <-shortCtx.Done(): - cancel() - return vterrors.Errorf(vtrpc.Code_ABORTED, "could not find healthy table for %v/%v%v: after: %v, aborting", sdw.cell, sdw.keyspace, sdw.sourceShard.Shard, *remoteActionsTimeout) - default: - sdw.sourceAlias, err = FindWorkerTablet(ctx, sdw.wr, sdw.cleaner, nil /* tsc */, sdw.cell, sdw.keyspace, sdw.sourceShard.Shard, sdw.minHealthyRdonlyTablets, topodatapb.TabletType_RDONLY) - if err != nil { - sdw.wr.Logger().Infof("FindWorkerTablet() failed for %v/%v/%v: %v retrying...", sdw.cell, sdw.keyspace, sdw.sourceShard.Shard, err) - continue - } - cancel() - return nil - } - } -} - -// synchronizeReplication phase: -// 1 - ask the primary of the destination shard to pause filtered replication, -// and return the source binlog positions -// (add a cleanup task to restart filtered replication on primary) -// 2 - stop the source tablet at a binlog position higher than the -// destination primary. Get that new list of positions. -// (add a cleanup task to restart binlog replication on the source tablet, and -// change the existing ChangeTabletType cleanup action to 'spare' type) -// 3 - ask the primary of the destination shard to resume filtered replication -// up to the new list of positions, and return its binlog position. -// 4 - wait until the destination tablet is equal or passed that primary -// binlog position, and stop its replication. -// (add a cleanup task to restart binlog replication on it, and change -// the existing ChangeTabletType cleanup action to 'spare' type) -// 5 - restart filtered replication on the destination primary. -// (remove the cleanup task that does the same) -// At this point, the source and the destination tablet are stopped at the same -// point. - -func (sdw *SplitDiffWorker) synchronizeReplication(ctx context.Context) error { - sdw.SetState(WorkerStateSyncReplication) - - shortCtx, cancel := context.WithTimeout(ctx, *remoteActionsTimeout) - defer cancel() - primaryInfo, err := sdw.wr.TopoServer().GetTablet(shortCtx, sdw.shardInfo.PrimaryAlias) - if err != nil { - return vterrors.Wrapf(err, "synchronizeReplication: cannot get Tablet record for primary %v", sdw.shardInfo.PrimaryAlias) - } - - // 1 - stop the primary binlog replication, get its current position - sdw.wr.Logger().Infof("Stopping primary binlog replication on %v", sdw.shardInfo.PrimaryAlias) - shortCtx, cancel = context.WithTimeout(ctx, *remoteActionsTimeout) - defer cancel() - _, err = sdw.wr.TabletManagerClient().VReplicationExec(shortCtx, primaryInfo.Tablet, binlogplayer.StopVReplication(sdw.sourceShard.Uid, "for split diff")) - if err != nil { - return vterrors.Wrapf(err, "VReplicationExec(stop) for %v failed", sdw.shardInfo.PrimaryAlias) - } - wrangler.RecordVReplicationAction(sdw.cleaner, primaryInfo.Tablet, binlogplayer.StartVReplication(sdw.sourceShard.Uid)) - p3qr, err := sdw.wr.TabletManagerClient().VReplicationExec(shortCtx, primaryInfo.Tablet, binlogplayer.ReadVReplicationPos(sdw.sourceShard.Uid)) - if err != nil { - return vterrors.Wrapf(err, "ReadVReplicationPos for %v failed", sdw.shardInfo.PrimaryAlias) - } - qr := sqltypes.Proto3ToResult(p3qr) - if len(qr.Rows) != 1 || len(qr.Rows[0]) != 1 { - return vterrors.Errorf(vtrpc.Code_INTERNAL, "unexpected result while reading position: %v", qr) - } - vreplicationPos := qr.Rows[0][0].ToString() - - // 2 - stop replication - sdw.wr.Logger().Infof("Stopping replica %v at a minimum of %v", sdw.sourceAlias, vreplicationPos) - // read the tablet - sourceTablet, err := sdw.wr.TopoServer().GetTablet(shortCtx, sdw.sourceAlias) - if err != nil { - return err - } - - shortCtx, cancel = context.WithTimeout(ctx, *remoteActionsTimeout) - defer cancel() - mysqlPos, err := sdw.wr.TabletManagerClient().StopReplicationMinimum(shortCtx, sourceTablet.Tablet, vreplicationPos, *remoteActionsTimeout) - if err != nil { - return vterrors.Wrapf(err, "cannot stop replica %v at right binlog position %v", sdw.sourceAlias, vreplicationPos) - } - - // change the cleaner actions from ChangeTabletType(rdonly) - // to StartReplication() + ChangeTabletType(spare) - wrangler.RecordStartReplicationAction(sdw.cleaner, sourceTablet.Tablet) - - // 3 - ask the primary of the destination shard to resume filtered - // replication up to the new list of positions - sdw.wr.Logger().Infof("Restarting primary %v until it catches up to %v", sdw.shardInfo.PrimaryAlias, mysqlPos) - shortCtx, cancel = context.WithTimeout(ctx, *remoteActionsTimeout) - defer cancel() - _, err = sdw.wr.TabletManagerClient().VReplicationExec(shortCtx, primaryInfo.Tablet, binlogplayer.StartVReplicationUntil(sdw.sourceShard.Uid, mysqlPos)) - if err != nil { - return vterrors.Wrapf(err, "VReplication(start until) for %v until %v failed", sdw.shardInfo.PrimaryAlias, mysqlPos) - } - if err := sdw.wr.TabletManagerClient().VReplicationWaitForPos(shortCtx, primaryInfo.Tablet, int(sdw.sourceShard.Uid), mysqlPos); err != nil { - return vterrors.Wrapf(err, "VReplicationWaitForPos for %v until %v failed", sdw.shardInfo.PrimaryAlias, mysqlPos) - } - primaryPos, err := sdw.wr.TabletManagerClient().PrimaryPosition(shortCtx, primaryInfo.Tablet) - if err != nil { - return vterrors.Wrapf(err, "PrimaryPosition for %v failed", sdw.shardInfo.PrimaryAlias) - } - - // 4 - wait until the destination tablet is equal or passed - // that primary binlog position, and stop its replication. - sdw.wr.Logger().Infof("Waiting for destination tablet %v to catch up to %v", sdw.destinationAlias, primaryPos) - shortCtx, cancel = context.WithTimeout(ctx, *remoteActionsTimeout) - defer cancel() - destinationTablet, err := sdw.wr.TopoServer().GetTablet(shortCtx, sdw.destinationAlias) - if err != nil { - return err - } - shortCtx, cancel = context.WithTimeout(ctx, *remoteActionsTimeout) - defer cancel() - if _, err = sdw.wr.TabletManagerClient().StopReplicationMinimum(shortCtx, destinationTablet.Tablet, primaryPos, *remoteActionsTimeout); err != nil { - return vterrors.Wrapf(err, "StopReplicationMinimum for %v at %v failed", sdw.destinationAlias, primaryPos) - } - wrangler.RecordStartReplicationAction(sdw.cleaner, destinationTablet.Tablet) - - // 5 - restart filtered replication on destination primary - sdw.wr.Logger().Infof("Restarting filtered replication on primary %v", sdw.shardInfo.PrimaryAlias) - shortCtx, cancel = context.WithTimeout(ctx, *remoteActionsTimeout) - defer cancel() - if _, err = sdw.wr.TabletManagerClient().VReplicationExec(shortCtx, primaryInfo.Tablet, binlogplayer.StartVReplication(sdw.sourceShard.Uid)); err != nil { - return vterrors.Wrapf(err, "VReplicationExec(start) failed for %v", sdw.shardInfo.PrimaryAlias) - } - - return nil -} - -// diff phase: will log messages regarding the diff. -// - get the schema on all tablets -// - if some table schema mismatches, record them (use existing schema diff tools). -// - for each table in destination, run a diff pipeline. - -func (sdw *SplitDiffWorker) diff(ctx context.Context) error { - sdw.SetState(WorkerStateDiff) - - sdw.wr.Logger().Infof("Gathering schema information...") - wg := sync.WaitGroup{} - rec := &concurrency.AllErrorRecorder{} - wg.Add(1) - go func() { - var err error - shortCtx, cancel := context.WithTimeout(ctx, *remoteActionsTimeout) - req := &tabletmanagerdatapb.GetSchemaRequest{ExcludeTables: sdw.excludeTables} - sdw.destinationSchemaDefinition, err = schematools.GetSchema( - shortCtx, sdw.wr.TopoServer(), sdw.wr.TabletManagerClient(), sdw.destinationAlias, req) - cancel() - if err != nil { - sdw.markAsWillFail(rec, err) - } - sdw.wr.Logger().Infof("Got schema from destination %v", sdw.destinationAlias) - wg.Done() - }() - wg.Add(1) - go func() { - var err error - shortCtx, cancel := context.WithTimeout(ctx, *remoteActionsTimeout) - req := &tabletmanagerdatapb.GetSchemaRequest{ExcludeTables: sdw.excludeTables} - sdw.sourceSchemaDefinition, err = schematools.GetSchema( - shortCtx, sdw.wr.TopoServer(), sdw.wr.TabletManagerClient(), sdw.sourceAlias, req) - cancel() - if err != nil { - sdw.markAsWillFail(rec, err) - } - sdw.wr.Logger().Infof("Got schema from source %v", sdw.sourceAlias) - wg.Done() - }() - - wg.Wait() - if rec.HasErrors() { - return rec.Error() - } - - // In splitClone state: - // if source destination shard table has column like: - // `object_id` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT - // Then after copy and exec it on destination the table column will turn to like this: - // `object_id` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT - // In that case,(mysql's behavior) when source has too many tables contains columns as `object_id`. - // there will be too much differ schema fail error out put on vtworkerclient side says: - // remainder of the error is truncated because gRPC has a size limit on errors - // This will obscure the real problem. - // so add this flag assumed people already know the schema does not match and make the process going on - if !sdw.skipVerify { - sdw.wr.Logger().Infof("Diffing the schema...") - rec = &concurrency.AllErrorRecorder{} - tmutils.DiffSchema("destination", sdw.destinationSchemaDefinition, "source", sdw.sourceSchemaDefinition, rec) - if !rec.HasErrors() { - sdw.wr.Logger().Infof("Schema match, good.") - } else { - sdw.wr.Logger().Warningf("Different schemas: %v", rec.Error().Error()) - return rec.Error() - } - } - - // read the vschema if needed - var keyspaceSchema *vindexes.KeyspaceSchema - kschema, err := sdw.wr.TopoServer().GetVSchema(ctx, sdw.keyspace) - if err != nil { - return vterrors.Wrapf(err, "cannot load VSchema for keyspace %v", sdw.keyspace) - } - if kschema == nil { - return vterrors.Errorf(vtrpc.Code_FAILED_PRECONDITION, "no VSchema for keyspace %v", sdw.keyspace) - } - - keyspaceSchema, err = vindexes.BuildKeyspaceSchema(kschema, sdw.keyspace) - if err != nil { - return vterrors.Wrapf(err, "cannot build vschema for keyspace %v", sdw.keyspace) - } - - // Compute the overlap keyrange. Later, we'll compare it with - // source or destination keyrange. If it matches either, - // we'll just ask for all the data. If the overlap is a subset, - // we'll filter. - overlap, err := key.KeyRangesOverlap(sdw.shardInfo.KeyRange, sdw.sourceShard.KeyRange) - if err != nil { - return vterrors.Wrap(err, "Source shard doesn't overlap with destination") - } - - // run the diffs, 8 at a time - sdw.wr.Logger().Infof("Running the diffs...") - sem := sync2.NewSemaphore(sdw.parallelDiffsCount, 0) - tableDefinitions := sdw.destinationSchemaDefinition.TableDefinitions - - // sort tables by size - // if there are large deltas between table sizes then it's more efficient to start working on the large tables first - sort.Slice(tableDefinitions, func(i, j int) bool { return tableDefinitions[i].DataLength > tableDefinitions[j].DataLength }) - - // use a channel to make sure tables are diffed in order - tableChan := make(chan *tabletmanagerdatapb.TableDefinition, len(tableDefinitions)) - for _, tableDefinition := range tableDefinitions { - tableChan <- tableDefinition - } - - // start as many goroutines as there are tables to diff - for range tableDefinitions { - wg.Add(1) - go func() { - defer wg.Done() - // use the semaphore to limit the number of tables that are diffed in parallel - sem.Acquire() - defer sem.Release() - - // grab the table to process out of the channel - tableDefinition := <-tableChan - - sdw.wr.Logger().Infof("Starting the diff on table %v", tableDefinition.Name) - - // On the source, see if we need a full scan - // or a filtered scan. - var sourceQueryResultReader *QueryResultReader - if key.KeyRangeEqual(overlap, sdw.sourceShard.KeyRange) { - sourceQueryResultReader, err = TableScan(ctx, sdw.wr.Logger(), sdw.wr.TopoServer(), sdw.sourceAlias, tableDefinition) - } else { - sourceQueryResultReader, err = TableScanByKeyRange(ctx, sdw.wr.Logger(), sdw.wr.TopoServer(), sdw.sourceAlias, tableDefinition, overlap, keyspaceSchema) - } - if err != nil { - newErr := vterrors.Wrap(err, "TableScan(ByKeyRange?)(source) failed") - sdw.markAsWillFail(rec, newErr) - sdw.wr.Logger().Error(newErr) - return - } - defer sourceQueryResultReader.Close(ctx) - - // On the destination, see if we need a full scan - // or a filtered scan. - var destinationQueryResultReader *QueryResultReader - if key.KeyRangeEqual(overlap, sdw.shardInfo.KeyRange) { - destinationQueryResultReader, err = TableScan(ctx, sdw.wr.Logger(), sdw.wr.TopoServer(), sdw.destinationAlias, tableDefinition) - } else { - destinationQueryResultReader, err = TableScanByKeyRange(ctx, sdw.wr.Logger(), sdw.wr.TopoServer(), sdw.destinationAlias, tableDefinition, overlap, keyspaceSchema) - } - if err != nil { - newErr := vterrors.Wrap(err, "TableScan(ByKeyRange?)(destination) failed") - sdw.markAsWillFail(rec, newErr) - sdw.wr.Logger().Error(newErr) - return - } - defer destinationQueryResultReader.Close(ctx) - - // Create the row differ. - differ, err := NewRowDiffer(sourceQueryResultReader, destinationQueryResultReader, tableDefinition) - if err != nil { - newErr := vterrors.Wrap(err, "NewRowDiffer() failed") - sdw.markAsWillFail(rec, newErr) - sdw.wr.Logger().Error(newErr) - return - } - - // And run the diff. - report, err := differ.Go(sdw.wr.Logger()) - if err != nil { - newErr := vterrors.Wrapf(err, "Differ.Go failed") - sdw.markAsWillFail(rec, newErr) - sdw.wr.Logger().Error(newErr) - } else { - if report.HasDifferences() { - err := vterrors.Errorf(vtrpc.Code_FAILED_PRECONDITION, "table %v has differences: %v", tableDefinition.Name, report.String()) - sdw.markAsWillFail(rec, err) - sdw.wr.Logger().Warningf(err.Error()) - } else { - sdw.wr.Logger().Infof("Table %v checks out (%v rows processed, %v qps)", tableDefinition.Name, report.processedRows, report.processingQPS) - } - } - }() - } - - // grab the table to process out of the channel - wg.Wait() - - return rec.Error() -} - -// markAsWillFail records the error and changes the state of the worker to reflect this -func (sdw *SplitDiffWorker) markAsWillFail(er concurrency.ErrorRecorder, err error) { - er.RecordError(err) - sdw.SetState(WorkerStateDiffWillFail) -} diff --git a/go/vt/worker/split_diff_cmd.go b/go/vt/worker/split_diff_cmd.go deleted file mode 100644 index 00e3a0ed304..00000000000 --- a/go/vt/worker/split_diff_cmd.go +++ /dev/null @@ -1,240 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package worker - -import ( - "flag" - "fmt" - "html/template" - "net/http" - "strconv" - "strings" - "sync" - - "vitess.io/vitess/go/vt/proto/vtrpc" - "vitess.io/vitess/go/vt/vterrors" - - "context" - - "vitess.io/vitess/go/vt/concurrency" - "vitess.io/vitess/go/vt/topo/topoproto" - "vitess.io/vitess/go/vt/wrangler" - - topodatapb "vitess.io/vitess/go/vt/proto/topodata" -) - -const splitDiffHTML = ` - - - Split Diff Action - - -

    Split Diff Action

    - - {{if .Error}} - Error: {{.Error}}
    - {{else}} - {{range $i, $si := .Shards}} -
  • {{$si.Keyspace}}/{{$si.Shard}}
  • - {{end}} - {{end}} - -` - -const splitDiffHTML2 = ` - - - Split Diff Action - - -

    Shard involved: {{.Keyspace}}/{{.Shard}}

    -

    Split Diff Action

    -
    - -
    - -
    - -
    - -
    - - - -
    - -` - -var splitDiffTemplate = mustParseTemplate("splitDiff", splitDiffHTML) -var splitDiffTemplate2 = mustParseTemplate("splitDiff2", splitDiffHTML2) - -func commandSplitDiff(wi *Instance, wr *wrangler.Wrangler, subFlags *flag.FlagSet, args []string) (Worker, error) { - sourceUID := subFlags.Int("source_uid", 0, "uid of the source shard to run the diff against") - excludeTables := subFlags.String("exclude_tables", "", "comma separated list of tables to exclude") - minHealthyRdonlyTablets := subFlags.Int("min_healthy_rdonly_tablets", defaultMinHealthyTablets, "minimum number of healthy RDONLY tablets before taking out one") - destTabletTypeStr := subFlags.String("dest_tablet_type", defaultDestTabletType, "destination tablet type (RDONLY or REPLICA) that will be used to compare the shards") - parallelDiffsCount := subFlags.Int("parallel_diffs_count", defaultParallelDiffsCount, "number of tables to diff in parallel") - skipVerify := subFlags.Bool("skip-verify", false, "skip verification of source and target schema when diff") - if err := subFlags.Parse(args); err != nil { - return nil, err - } - if subFlags.NArg() != 1 { - subFlags.Usage() - return nil, vterrors.New(vtrpc.Code_INVALID_ARGUMENT, "command SplitDiff requires ") - } - keyspace, shard, err := topoproto.ParseKeyspaceShard(subFlags.Arg(0)) - if err != nil { - return nil, err - } - var excludeTableArray []string - if *excludeTables != "" { - excludeTableArray = strings.Split(*excludeTables, ",") - } - - destTabletType, ok := topodatapb.TabletType_value[*destTabletTypeStr] - if !ok { - return nil, vterrors.Errorf(vtrpc.Code_INVALID_ARGUMENT, "command SplitDiff invalid dest_tablet_type: %v", destTabletType) - } - - return NewSplitDiffWorker(wr, wi.cell, keyspace, shard, uint32(*sourceUID), excludeTableArray, *minHealthyRdonlyTablets, *parallelDiffsCount, topodatapb.TabletType(destTabletType), *skipVerify), nil -} - -// shardsWithSources returns all the shards that have SourceShards set -// with no Tables list. -func shardsWithSources(ctx context.Context, wr *wrangler.Wrangler) ([]map[string]string, error) { - shortCtx, cancel := context.WithTimeout(ctx, *remoteActionsTimeout) - keyspaces, err := wr.TopoServer().GetKeyspaces(shortCtx) - cancel() - if err != nil { - return nil, vterrors.Wrap(err, "failed to get list of keyspaces") - } - - wg := sync.WaitGroup{} - mu := sync.Mutex{} // protects result - result := make([]map[string]string, 0, len(keyspaces)) - rec := concurrency.AllErrorRecorder{} - for _, keyspace := range keyspaces { - wg.Add(1) - go func(keyspace string) { - defer wg.Done() - shortCtx, cancel := context.WithTimeout(ctx, *remoteActionsTimeout) - shards, err := wr.TopoServer().GetShardNames(shortCtx, keyspace) - cancel() - if err != nil { - rec.RecordError(vterrors.Wrapf(err, "failed to get list of shards for keyspace '%v'", keyspace)) - return - } - for _, shard := range shards { - wg.Add(1) - go func(keyspace, shard string) { - defer wg.Done() - shortCtx, cancel := context.WithTimeout(ctx, *remoteActionsTimeout) - si, err := wr.TopoServer().GetShard(shortCtx, keyspace, shard) - cancel() - if err != nil { - rec.RecordError(vterrors.Wrapf(err, "failed to get details for shard '%v'", topoproto.KeyspaceShardString(keyspace, shard))) - return - } - - if len(si.SourceShards) > 0 && len(si.SourceShards[0].Tables) == 0 { - mu.Lock() - result = append(result, map[string]string{ - "Keyspace": keyspace, - "Shard": shard, - }) - mu.Unlock() - } - }(keyspace, shard) - } - }(keyspace) - } - wg.Wait() - - if rec.HasErrors() { - return nil, rec.Error() - } - if len(result) == 0 { - return nil, vterrors.New(vtrpc.Code_FAILED_PRECONDITION, "there are no shards with SourceShards") - } - return result, nil -} - -func interactiveSplitDiff(ctx context.Context, wi *Instance, wr *wrangler.Wrangler, w http.ResponseWriter, r *http.Request) (Worker, *template.Template, map[string]any, error) { - if err := r.ParseForm(); err != nil { - return nil, nil, nil, vterrors.Wrap(err, "cannot parse form") - } - keyspace := r.FormValue("keyspace") - shard := r.FormValue("shard") - - if keyspace == "" || shard == "" { - // display the list of possible shards to chose from - result := make(map[string]any) - shards, err := shardsWithSources(ctx, wr) - if err != nil { - result["Error"] = err.Error() - } else { - result["Shards"] = shards - } - return nil, splitDiffTemplate, result, nil - } - - submitButtonValue := r.FormValue("submit") - if submitButtonValue == "" { - // display the input form - result := make(map[string]any) - result["Keyspace"] = keyspace - result["Shard"] = shard - result["DefaultSourceUID"] = "0" - result["DefaultMinHealthyRdonlyTablets"] = fmt.Sprintf("%v", defaultMinHealthyTablets) - result["DefaultParallelDiffsCount"] = fmt.Sprintf("%v", defaultParallelDiffsCount) - return nil, splitDiffTemplate2, result, nil - } - - // Process input form. - sourceUIDStr := r.FormValue("sourceUID") - sourceUID, err := strconv.ParseInt(sourceUIDStr, 0, 64) - if err != nil { - return nil, nil, nil, vterrors.Wrap(err, "cannot parse sourceUID") - } - excludeTables := r.FormValue("excludeTables") - var excludeTableArray []string - if excludeTables != "" { - excludeTableArray = strings.Split(excludeTables, ",") - } - minHealthyRdonlyTabletsStr := r.FormValue("minHealthyRdonlyTablets") - parallelDiffsCountStr := r.FormValue("parallelDiffsCount") - minHealthyRdonlyTablets, err := strconv.ParseInt(minHealthyRdonlyTabletsStr, 0, 64) - if err != nil { - return nil, nil, nil, vterrors.Wrap(err, "cannot parse minHealthyRdonlyTablets") - } - parallelDiffsCount, err := strconv.ParseInt(parallelDiffsCountStr, 0, 64) - if err != nil { - return nil, nil, nil, vterrors.Wrap(err, "cannot parse parallelDiffsCount") - } - - // start the diff job - // TODO: @rafael - Add option to set destination tablet type in UI form. - wrk := NewSplitDiffWorker(wr, wi.cell, keyspace, shard, uint32(sourceUID), excludeTableArray, int(minHealthyRdonlyTablets), int(parallelDiffsCount), topodatapb.TabletType_RDONLY, false) - return wrk, nil, nil, nil -} - -func init() { - AddCommand("Diffs", Command{"SplitDiff", - commandSplitDiff, interactiveSplitDiff, - "[--exclude_tables=''] ", - "Diffs a rdonly destination shard against its SourceShards"}) -} diff --git a/go/vt/worker/split_diff_test.go b/go/vt/worker/split_diff_test.go deleted file mode 100644 index 0b23831c948..00000000000 --- a/go/vt/worker/split_diff_test.go +++ /dev/null @@ -1,310 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package worker - -import ( - "context" - "fmt" - "strings" - "testing" - "time" - - "vitess.io/vitess/go/sqltypes" - "vitess.io/vitess/go/vt/discovery" - "vitess.io/vitess/go/vt/logutil" - "vitess.io/vitess/go/vt/mysqlctl/tmutils" - "vitess.io/vitess/go/vt/topo/memorytopo" - "vitess.io/vitess/go/vt/topotools" - "vitess.io/vitess/go/vt/vttablet/grpcqueryservice" - "vitess.io/vitess/go/vt/vttablet/queryservice/fakes" - "vitess.io/vitess/go/vt/wrangler" - "vitess.io/vitess/go/vt/wrangler/testlib" - - querypb "vitess.io/vitess/go/vt/proto/query" - tabletmanagerdatapb "vitess.io/vitess/go/vt/proto/tabletmanagerdata" - topodatapb "vitess.io/vitess/go/vt/proto/topodata" - vschemapb "vitess.io/vitess/go/vt/proto/vschema" -) - -// destinationTabletServer is a local QueryService implementation to -// support the tests -type destinationTabletServer struct { - t *testing.T - - *fakes.StreamHealthQueryService - excludedTable string -} - -func (sq *destinationTabletServer) StreamExecute(ctx context.Context, target *querypb.Target, sql string, bindVariables map[string]*querypb.BindVariable, transactionID int64, reservedID int64, options *querypb.ExecuteOptions, callback func(*sqltypes.Result) error) error { - if strings.Contains(sql, sq.excludedTable) { - sq.t.Errorf("Split Diff operation on destination should skip the excluded table: %v query: %v", sq.excludedTable, sql) - } - - if hasKeyspace := strings.Contains(sql, "WHERE `keyspace_id`"); hasKeyspace == true { - sq.t.Errorf("Sql query on destination should not contain a keyspace_id WHERE clause; query received: %v", sql) - } - - sq.t.Logf("destinationTabletServer: got query: %v", sql) - - // Send the headers - if err := callback(&sqltypes.Result{ - Fields: []*querypb.Field{ - { - Name: "id", - Type: sqltypes.Int64, - }, - { - Name: "msg", - Type: sqltypes.VarChar, - }, - { - Name: "keyspace_id", - Type: sqltypes.Int64, - }, - }, - }); err != nil { - return err - } - - // Send the values - ksids := []uint64{0x2000000000000000, 0x6000000000000000} - for i := 0; i < 100; i++ { - // skip the out-of-range values - if i%2 == 1 { - continue - } - if err := callback(&sqltypes.Result{ - Rows: [][]sqltypes.Value{ - { - sqltypes.NewVarBinary(fmt.Sprintf("%v", i)), - sqltypes.NewVarBinary(fmt.Sprintf("Text for %v", i)), - sqltypes.NewVarBinary(fmt.Sprintf("%v", ksids[i%2])), - }, - }, - }); err != nil { - return err - } - } - return nil -} - -// sourceTabletServer is a local QueryService implementation to support the tests -type sourceTabletServer struct { - t *testing.T - - *fakes.StreamHealthQueryService - excludedTable string - v3 bool -} - -func (sq *sourceTabletServer) StreamExecute(ctx context.Context, target *querypb.Target, sql string, bindVariables map[string]*querypb.BindVariable, transactionID int64, reservedID int64, options *querypb.ExecuteOptions, callback func(*sqltypes.Result) error) error { - if strings.Contains(sql, sq.excludedTable) { - sq.t.Errorf("Split Diff operation on source should skip the excluded table: %v query: %v", sq.excludedTable, sql) - } - - // we test for a keyspace_id where clause, except for v3 - if !sq.v3 { - if hasKeyspace := strings.Contains(sql, "WHERE `keyspace_id` < 4611686018427387904"); hasKeyspace != true { - sq.t.Errorf("Sql query on source should contain a keyspace_id WHERE clause; query received: %v", sql) - } - } - - sq.t.Logf("sourceTabletServer: got query: %v", sql) - - // Send the headers - if err := callback(&sqltypes.Result{ - Fields: []*querypb.Field{ - { - Name: "id", - Type: sqltypes.Int64, - }, - { - Name: "msg", - Type: sqltypes.VarChar, - }, - { - Name: "keyspace_id", - Type: sqltypes.Int64, - }, - }, - }); err != nil { - return err - } - - // Send the values - ksids := []uint64{0x2000000000000000, 0x6000000000000000} - for i := 0; i < 100; i++ { - if !sq.v3 && i%2 == 1 { - // for v2, filtering is done at SQL layer - continue - } - if err := callback(&sqltypes.Result{ - Rows: [][]sqltypes.Value{ - { - sqltypes.NewVarBinary(fmt.Sprintf("%v", i)), - sqltypes.NewVarBinary(fmt.Sprintf("Text for %v", i)), - sqltypes.NewVarBinary(fmt.Sprintf("%v", ksids[i%2])), - }, - }, - }); err != nil { - return err - } - } - return nil -} - -// TODO(aaijazi): Create a test in which source and destination data does not match - -func testSplitDiff(t *testing.T, destinationTabletType topodatapb.TabletType) { - delay := discovery.GetTabletPickerRetryDelay() - defer func() { - discovery.SetTabletPickerRetryDelay(delay) - }() - discovery.SetTabletPickerRetryDelay(5 * time.Millisecond) - - ts := memorytopo.NewServer("cell1", "cell2") - ctx := context.Background() - wi := NewInstance(ts, "cell1", time.Second) - - if err := ts.CreateKeyspace(ctx, "ks", &topodatapb.Keyspace{}); err != nil { - t.Fatalf("CreateKeyspace v3 failed: %v", err) - } - - vs := &vschemapb.Keyspace{ - Sharded: true, - Vindexes: map[string]*vschemapb.Vindex{ - "table1_index": { - Type: "numeric", - }, - }, - Tables: map[string]*vschemapb.Table{ - "table1": { - ColumnVindexes: []*vschemapb.ColumnVindex{ - { - Column: "keyspace_id", - Name: "table1_index", - }, - }, - }, - }, - } - if err := ts.SaveVSchema(ctx, "ks", vs); err != nil { - t.Fatalf("SaveVSchema v3 failed: %v", err) - } - - sourcePrimary := testlib.NewFakeTablet(t, wi.wr, "cell1", 0, - topodatapb.TabletType_PRIMARY, nil, testlib.TabletKeyspaceShard(t, "ks", "-80")) - sourceRdonly1 := testlib.NewFakeTablet(t, wi.wr, "cell1", 1, - topodatapb.TabletType_RDONLY, nil, testlib.TabletKeyspaceShard(t, "ks", "-80")) - sourceRdonly2 := testlib.NewFakeTablet(t, wi.wr, "cell1", 2, - topodatapb.TabletType_RDONLY, nil, testlib.TabletKeyspaceShard(t, "ks", "-80")) - - leftPrimary := testlib.NewFakeTablet(t, wi.wr, "cell1", 10, - topodatapb.TabletType_PRIMARY, nil, testlib.TabletKeyspaceShard(t, "ks", "-40")) - leftRdonly1 := testlib.NewFakeTablet(t, wi.wr, "cell1", 11, - destinationTabletType, nil, testlib.TabletKeyspaceShard(t, "ks", "-40")) - leftRdonly2 := testlib.NewFakeTablet(t, wi.wr, "cell1", 12, - destinationTabletType, nil, testlib.TabletKeyspaceShard(t, "ks", "-40")) - - // add the topo and schema data we'll need - if err := ts.CreateShard(ctx, "ks", "80-"); err != nil { - t.Fatalf("CreateShard(\"-80\") failed: %v", err) - } - wi.wr.SetSourceShards(ctx, "ks", "-40", []*topodatapb.TabletAlias{sourceRdonly1.Tablet.Alias}, nil) - if err := topotools.RebuildKeyspace(ctx, wi.wr.Logger(), wi.wr.TopoServer(), "ks", nil, false); err != nil { - t.Fatalf("RebuildKeyspaceGraph failed: %v", err) - } - - excludedTable := "excludedTable1" - - for _, rdonly := range []*testlib.FakeTablet{sourceRdonly1, sourceRdonly2, leftRdonly1, leftRdonly2} { - // The destination only has half the data. - // For v2, we do filtering at the SQl level. - // For v3, we do it in the client. - // So in any case, we need real data. - rdonly.FakeMysqlDaemon.Schema = &tabletmanagerdatapb.SchemaDefinition{ - DatabaseSchema: "", - TableDefinitions: []*tabletmanagerdatapb.TableDefinition{ - { - Name: "table1", - Columns: []string{"id", "msg", "keyspace_id"}, - PrimaryKeyColumns: []string{"id"}, - Type: tmutils.TableBaseTable, - }, - { - Name: excludedTable, - Columns: []string{"id", "msg", "keyspace_id"}, - PrimaryKeyColumns: []string{"id"}, - Type: tmutils.TableBaseTable, - }, - }, - } - } - - for _, sourceRdonly := range []*testlib.FakeTablet{sourceRdonly1, sourceRdonly2} { - qs := fakes.NewStreamHealthQueryService(sourceRdonly.Target()) - qs.AddDefaultHealthResponse() - grpcqueryservice.Register(sourceRdonly.RPCServer, &sourceTabletServer{ - t: t, - - StreamHealthQueryService: qs, - excludedTable: excludedTable, - v3: true, - }) - } - - for _, destRdonly := range []*testlib.FakeTablet{leftRdonly1, leftRdonly2} { - qs := fakes.NewStreamHealthQueryService(destRdonly.Target()) - qs.AddDefaultHealthResponse() - grpcqueryservice.Register(destRdonly.RPCServer, &destinationTabletServer{ - t: t, - - StreamHealthQueryService: qs, - excludedTable: excludedTable, - }) - } - - // Start action loop after having registered all RPC services. - for _, ft := range []*testlib.FakeTablet{sourcePrimary, sourceRdonly1, sourceRdonly2, leftPrimary, leftRdonly1, leftRdonly2} { - ft.StartActionLoop(t, wi.wr) - defer ft.StopActionLoop(t) - } - - tabletTypeName := topodatapb.TabletType_name[int32(destinationTabletType)] - // Run the vtworker command. - args := []string{ - "SplitDiff", - "-exclude_tables", excludedTable, - "-dest_tablet_type", tabletTypeName, - "ks/-40", - } - // We need to use FakeTabletManagerClient because we don't - // have a good way to fake the binlog player yet, which is - // necessary for synchronizing replication. - wr := wrangler.New(logutil.NewConsoleLogger(), ts, newFakeTMCTopo(ts)) - if err := runCommand(t, wi, wr, args); err != nil { - t.Fatal(err) - } -} - -func TestSplitDiffv3(t *testing.T) { - testSplitDiff(t, topodatapb.TabletType_RDONLY) -} - -func TestSplitDiffWithReplica(t *testing.T) { - testSplitDiff(t, topodatapb.TabletType_REPLICA) -} diff --git a/go/vt/worker/status.go b/go/vt/worker/status.go deleted file mode 100644 index 546cc079bb8..00000000000 --- a/go/vt/worker/status.go +++ /dev/null @@ -1,146 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package worker - -import ( - "fmt" - "html/template" - "net/http" - "strings" - - "vitess.io/vitess/go/acl" - "vitess.io/vitess/go/vt/servenv" -) - -const workerStatusPartHTML = servenv.JQueryIncludes + ` - -
    -` - -const workerStatusHTML = ` - - -Worker Status - - -{{if .Status}} -

    Worker status:

    -
    - {{.Status}} -
    -

    Worker logs:

    -
    - {{.Logs}} -
    - {{if .Done}} -

    Reset Job

    - {{else}} -

    Cancel Job

    - {{end}} -{{else}} -

    This worker is idle.

    -

    Toplevel Menu

    -{{end}} - - -` - -// InitStatusHandling installs webserver handlers for global actions like /status, /reset and /cancel. -func (wi *Instance) InitStatusHandling() { - // code to serve /status - workerTemplate := mustParseTemplate("worker", workerStatusHTML) - http.HandleFunc("/status", func(w http.ResponseWriter, r *http.Request) { - if err := acl.CheckAccessHTTP(r, acl.ADMIN); err != nil { - acl.SendError(w, err) - return - } - - wi.currentWorkerMutex.Lock() - wrk := wi.currentWorker - logger := wi.currentMemoryLogger - ctx := wi.currentContext - err := wi.lastRunError - stopTime := wi.lastRunStopTime - wi.currentWorkerMutex.Unlock() - - data := make(map[string]any) - if wrk != nil { - status := template.HTML("Current worker:
    \n") + wrk.StatusAsHTML() - if ctx == nil { - data["Done"] = true - if err != nil { - status += template.HTML(fmt.Sprintf("
    \nEnded with an error: %v
    \n", err)) - } - status += template.HTML(fmt.Sprintf("
    \nEnd Time: %v
    \n", stopTime)) - } - data["Status"] = status - if logger != nil { - data["Logs"] = template.HTML(strings.Replace(logger.String(), "\n", "
    \n", -1)) - } else { - data["Logs"] = template.HTML("See console for logs
    \n") - } - } - executeTemplate(w, workerTemplate, data) - }) - - // add the section in status that does auto-refresh of status div - servenv.AddStatusPart("Worker Status", workerStatusPartHTML, func() any { - return nil - }) - - // reset handler - http.HandleFunc("/reset", func(w http.ResponseWriter, r *http.Request) { - if err := acl.CheckAccessHTTP(r, acl.ADMIN); err != nil { - acl.SendError(w, err) - return - } - - if err := wi.Reset(); err != nil { - httpError(w, err.Error(), nil) - } else { - // No worker currently running, we go to the menu. - http.Redirect(w, r, "/", http.StatusTemporaryRedirect) - } - }) - - // cancel handler - http.HandleFunc("/cancel", func(w http.ResponseWriter, r *http.Request) { - if err := acl.CheckAccessHTTP(r, acl.ADMIN); err != nil { - acl.SendError(w, err) - return - } - - if wi.Cancel() { - // We canceled the running worker. Go back to the status page. - http.Redirect(w, r, servenv.StatusURLPath(), http.StatusTemporaryRedirect) - } else { - // No worker, or not running, we go to the menu. - http.Redirect(w, r, "/", http.StatusTemporaryRedirect) - } - }) -} diff --git a/go/vt/worker/status_worker.go b/go/vt/worker/status_worker.go deleted file mode 100644 index 8649e3d3985..00000000000 --- a/go/vt/worker/status_worker.go +++ /dev/null @@ -1,118 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package worker - -import ( - "html/template" - "sync" -) - -// StatusWorkerState is the type for a StatusWorker's status -type StatusWorkerState string - -// All possible status strings (if your implementation needs more, -// just add them). - -const ( - // WorkerStateNotStarted is the initial state. - WorkerStateNotStarted StatusWorkerState = "not started" - // WorkerStateDone is set when the worker successfully finished. - WorkerStateDone StatusWorkerState = "done" - // WorkerStateError is set when the worker failed. - WorkerStateError StatusWorkerState = "error" - // WorkerStateInit is set when the worker does initialize its state. - WorkerStateInit StatusWorkerState = "initializing" - // WorkerStateFindTargets is set when the worker searches healthy RDONLY tablets. - WorkerStateFindTargets StatusWorkerState = "finding target instances" - // WorkerStateSyncReplication is set when the worker ensures that source and - // destination tablets are at the same GTID during the diff. - WorkerStateSyncReplication StatusWorkerState = "synchronizing replication" - - // WorkerStateCloneOnline is set when the worker copies the data in the online phase. - WorkerStateCloneOnline StatusWorkerState = "cloning the data (online)" - // WorkerStateCloneOffline is set when the worker copies the data in the offline phase. - WorkerStateCloneOffline StatusWorkerState = "cloning the data (offline)" - - // WorkerStateDiff is set when the worker compares the data. - WorkerStateDiff StatusWorkerState = "running the diff" - - // WorkerStateDiffWillFail is set when the worker is still comparing the data, but we have already found discrepancies. - WorkerStateDiffWillFail StatusWorkerState = "running the diff, already found differences" - - // WorkerStateDebugRunning is set when an internal command (e.g. Block or Ping) is currently running. - WorkerStateDebugRunning StatusWorkerState = "running an internal debug command" - - // WorkerStateCleanUp is set when the worker reverses the initialization e.g. - // the type of a taken out RDONLY tablet is changed back from "worker" to "spare". - WorkerStateCleanUp StatusWorkerState = "cleaning up" -) - -func (state StatusWorkerState) String() string { - return string(state) -} - -// StatusWorker is the base type for a worker which keeps a status. -// The status is protected by a mutex. -// StatusWorker also provides default implementations for StatusAsHTML -// and StatusAsText to make it easier on workers if they don't need to -// export more. -type StatusWorker struct { - mu *sync.Mutex - // state contains the worker's current state. Guarded by mu. - state StatusWorkerState -} - -// NewStatusWorker returns a StatusWorker in state WorkerStateNotStarted. -func NewStatusWorker() StatusWorker { - return StatusWorker{ - mu: &sync.Mutex{}, - state: WorkerStateNotStarted, - } -} - -// SetState is a convenience function for workers. -func (w *StatusWorker) SetState(state StatusWorkerState) { - w.mu.Lock() - defer w.mu.Unlock() - - w.state = state - statsState.Set(string(state)) -} - -// State is part of the Worker interface. -func (w *StatusWorker) State() StatusWorkerState { - w.mu.Lock() - defer w.mu.Unlock() - - return w.state -} - -// StatusAsHTML is part of the Worker interface. -func (w *StatusWorker) StatusAsHTML() template.HTML { - w.mu.Lock() - defer w.mu.Unlock() - - return template.HTML("State: " + w.state.String() + "
    \n") -} - -// StatusAsText is part of the Worker interface. -func (w *StatusWorker) StatusAsText() string { - w.mu.Lock() - defer w.mu.Unlock() - - return "State: " + w.state.String() + "\n" -} diff --git a/go/vt/worker/table_status.go b/go/vt/worker/table_status.go deleted file mode 100644 index 2fb32800646..00000000000 --- a/go/vt/worker/table_status.go +++ /dev/null @@ -1,193 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package worker - -import ( - "fmt" - "sync" - "time" - - "vitess.io/vitess/go/vt/mysqlctl/tmutils" - "vitess.io/vitess/go/vt/proto/vtrpc" - "vitess.io/vitess/go/vt/vterrors" - - tabletmanagerdatapb "vitess.io/vitess/go/vt/proto/tabletmanagerdata" -) - -// tableStatusList contains the status for each table of a schema. -// Functions which modify the status of a table must use the same index for -// the table which the table had in schema passed in to initialize(). -type tableStatusList struct { - // mu guards all fields in the group below. - mu sync.Mutex - // initialized is true when initialize() was called. - initialized bool - // tableStatuses is written once by initialize(). - tableStatuses []*tableStatus - // startTime records the time initialize() was called. - // Same as tableStatuses it's a write-once field. - startTime time.Time -} - -func (t *tableStatusList) initialize(schema *tabletmanagerdatapb.SchemaDefinition) { - t.mu.Lock() - defer t.mu.Unlock() - - if t.initialized { - panic(vterrors.Errorf(vtrpc.Code_FAILED_PRECONDITION, "tableStatusList is already initialized: %v", t.tableStatuses)) - } - - t.tableStatuses = make([]*tableStatus, len(schema.TableDefinitions)) - for i, td := range schema.TableDefinitions { - t.tableStatuses[i] = newTableStatus(td.Name, td.Type != tmutils.TableBaseTable /* isView */, td.RowCount) - } - - t.startTime = time.Now() - - t.initialized = true -} - -// isInitialized returns true when initialize() was called. -func (t *tableStatusList) isInitialized() bool { - t.mu.Lock() - defer t.mu.Unlock() - return t.initialized -} - -func (t *tableStatusList) setThreadCount(tableIndex, threadCount int) { - if !t.isInitialized() { - panic("setThreadCount() requires an initialized tableStatusList") - } - - t.tableStatuses[tableIndex].setThreadCount(threadCount) -} - -func (t *tableStatusList) threadStarted(tableIndex int) { - if !t.isInitialized() { - panic("threadStarted() requires an initialized tableStatusList") - } - - t.tableStatuses[tableIndex].threadStarted() -} - -func (t *tableStatusList) threadDone(tableIndex int) { - if !t.isInitialized() { - panic("threadDone() requires an initialized tableStatusList") - } - - t.tableStatuses[tableIndex].threadDone() -} - -func (t *tableStatusList) addCopiedRows(tableIndex, copiedRows int) { - if !t.isInitialized() { - panic("addCopiedRows() requires an initialized tableStatusList") - } - - t.tableStatuses[tableIndex].addCopiedRows(copiedRows) -} - -// format returns a status for each table and the overall ETA. -func (t *tableStatusList) format() ([]string, time.Time) { - if !t.isInitialized() { - return nil, time.Now() - } - - copiedRows := uint64(0) - rowCount := uint64(0) - result := make([]string, len(t.tableStatuses)) - for i, ts := range t.tableStatuses { - ts.mu.Lock() - if ts.isView { - // views are not copied - result[i] = fmt.Sprintf("%v is a view", ts.name) - } else if ts.threadsStarted == 0 { - // we haven't started yet - result[i] = fmt.Sprintf("%v: copy not started (estimating %v rows)", ts.name, ts.rowCount) - } else if ts.threadsDone == ts.threadCount { - // we are done with the copy - result[i] = fmt.Sprintf("%v: copy done, processed %v rows", ts.name, ts.copiedRows) - } else { - // copy is running - // Display 0% if rowCount is 0 because the actual number of rows can be > 0 - // due to InnoDB's imperfect statistics. - percentage := 0.0 - if ts.rowCount > 0 { - percentage = float64(ts.copiedRows) / float64(ts.rowCount) * 100.0 - } - result[i] = fmt.Sprintf("%v: copy running using %v threads (%v/%v rows processed, %.1f%%)", ts.name, ts.threadsStarted-ts.threadsDone, ts.copiedRows, ts.rowCount, percentage) - } - copiedRows += ts.copiedRows - rowCount += ts.rowCount - ts.mu.Unlock() - } - now := time.Now() - if rowCount == 0 || copiedRows == 0 { - return result, now - } - eta := now.Add(time.Duration(float64(now.Sub(t.startTime)) * float64(rowCount) / float64(copiedRows))) - return result, eta -} - -// tableStatus keeps track of the status for a given table. -type tableStatus struct { - name string - isView bool - - // mu guards all fields in the group below. - mu sync.Mutex - rowCount uint64 // set to approximate value, until copy ends - copiedRows uint64 // actual count of copied rows - threadCount int // how many concurrent threads will copy the data - threadsStarted int // how many threads have started - threadsDone int // how many threads are done -} - -func newTableStatus(name string, isView bool, rowCount uint64) *tableStatus { - return &tableStatus{ - name: name, - isView: isView, - rowCount: rowCount, - } -} - -func (ts *tableStatus) setThreadCount(threadCount int) { - ts.mu.Lock() - ts.threadCount = threadCount - ts.mu.Unlock() -} - -func (ts *tableStatus) threadStarted() { - ts.mu.Lock() - ts.threadsStarted++ - ts.mu.Unlock() -} - -func (ts *tableStatus) threadDone() { - ts.mu.Lock() - ts.threadsDone++ - ts.mu.Unlock() -} - -func (ts *tableStatus) addCopiedRows(copiedRows int) { - ts.mu.Lock() - ts.copiedRows += uint64(copiedRows) - if ts.copiedRows > ts.rowCount { - // since rowCount is not accurate, update it if we go past it. - ts.rowCount = ts.copiedRows - } - ts.mu.Unlock() -} diff --git a/go/vt/worker/tablet_provider.go b/go/vt/worker/tablet_provider.go deleted file mode 100644 index 8c07e18407f..00000000000 --- a/go/vt/worker/tablet_provider.go +++ /dev/null @@ -1,104 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package worker - -import ( - "fmt" - - "vitess.io/vitess/go/vt/vterrors" - - "context" - - "vitess.io/vitess/go/vt/discovery" - "vitess.io/vitess/go/vt/topo" - "vitess.io/vitess/go/vt/topo/topoproto" - - topodatapb "vitess.io/vitess/go/vt/proto/topodata" -) - -// tabletProvider defines an interface to pick a tablet for reading data. -type tabletProvider interface { - // getTablet returns a tablet. - getTablet() (*topodatapb.Tablet, error) - - // returnTablet must be called after the tablet is no longer used and e.g. - // TabletTracker.Untrack() should get called for it. - returnTablet(*topodatapb.Tablet) - - // description returns a string which can be used in error messages e.g. - // the name of the keyspace and the shard. - description() string -} - -// singleTabletProvider implements the tabletProvider interface and always -// returns the one tablet which was set at creation. -type singleTabletProvider struct { - ctx context.Context - ts *topo.Server - alias *topodatapb.TabletAlias -} - -func newSingleTabletProvider(ctx context.Context, ts *topo.Server, alias *topodatapb.TabletAlias) *singleTabletProvider { - return &singleTabletProvider{ctx, ts, alias} -} - -func (p *singleTabletProvider) getTablet() (*topodatapb.Tablet, error) { - shortCtx, cancel := context.WithTimeout(p.ctx, *remoteActionsTimeout) - tablet, err := p.ts.GetTablet(shortCtx, p.alias) - cancel() - if err != nil { - return nil, vterrors.Wrapf(err, "failed to resolve tablet alias: %v err", topoproto.TabletAliasString(p.alias)) - } - return tablet.Tablet, err -} - -func (p *singleTabletProvider) returnTablet(*topodatapb.Tablet) {} - -func (p *singleTabletProvider) description() string { - return topoproto.TabletAliasString(p.alias) -} - -// shardTabletProvider returns a random healthy RDONLY tablet for a given -// keyspace and shard. It uses the LegacyHealthCheck module to retrieve the tablets. -type shardTabletProvider struct { - tsc *discovery.LegacyTabletStatsCache - tracker *TabletTracker - keyspace string - shard string - tabletType topodatapb.TabletType -} - -func newShardTabletProvider(tsc *discovery.LegacyTabletStatsCache, tracker *TabletTracker, keyspace, shard string, tabletType topodatapb.TabletType) *shardTabletProvider { - return &shardTabletProvider{tsc, tracker, keyspace, shard, tabletType} -} - -func (p *shardTabletProvider) getTablet() (*topodatapb.Tablet, error) { - // Pick any healthy serving tablet. - tablets := p.tsc.GetHealthyTabletStats(p.keyspace, p.shard, p.tabletType) - if len(tablets) == 0 { - return nil, fmt.Errorf("%v: no healthy %v tablets available", p.description(), p.tabletType) - } - return p.tracker.Track(tablets), nil -} - -func (p *shardTabletProvider) returnTablet(t *topodatapb.Tablet) { - p.tracker.Untrack(t.Alias) -} - -func (p *shardTabletProvider) description() string { - return topoproto.KeyspaceShardString(p.keyspace, p.shard) -} diff --git a/go/vt/worker/tablet_tracker.go b/go/vt/worker/tablet_tracker.go deleted file mode 100644 index 3e2dee1ba61..00000000000 --- a/go/vt/worker/tablet_tracker.go +++ /dev/null @@ -1,150 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package worker - -import ( - "fmt" - "sort" - "strings" - "sync" - - "vitess.io/vitess/go/vt/discovery" - "vitess.io/vitess/go/vt/topo/topoproto" - - topodatapb "vitess.io/vitess/go/vt/proto/topodata" -) - -// TabletTracker tracks for each tablet alias how often it is currently in use -// for a streaming read query. -// By using this information, all streaming queries should be balanced across -// all available tablets. -type TabletTracker struct { - // mu guards the fields below. - mu sync.Mutex - // usedTablets stores how often a tablet is currently in use. - // The map key is the string of the TabletAlias. - usedTablets map[string]int -} - -// NewTabletTracker returns a new TabletTracker. -func NewTabletTracker() *TabletTracker { - return &TabletTracker{ - usedTablets: make(map[string]int), - } -} - -// Track will pick the least used tablet from "stats", increment its usage by 1 -// and return it. -// "stats" must not be empty. -func (t *TabletTracker) Track(stats []discovery.LegacyTabletStats) *topodatapb.Tablet { - if len(stats) == 0 { - panic("stats must not be empty") - } - t.mu.Lock() - defer t.mu.Unlock() - - // Try to find a tablet which is not in use yet. - for _, stat := range stats { - key := topoproto.TabletAliasString(stat.Tablet.Alias) - if _, ok := t.usedTablets[key]; !ok { - t.usedTablets[key] = 1 - return stat.Tablet - } - } - - // If we reached this point, "stats" is a subset of "usedTablets" i.e. - // all tablets are already in use. Take the least used one. - for _, aliasString := range t.tabletsByUsage() { - for _, stat := range stats { - key := topoproto.TabletAliasString(stat.Tablet.Alias) - if key == aliasString { - t.usedTablets[key]++ - return stat.Tablet - } - } - } - panic("BUG: we did not add any tablet") -} - -// Untrack decrements the usage of "alias" by 1. -func (t *TabletTracker) Untrack(alias *topodatapb.TabletAlias) { - t.mu.Lock() - defer t.mu.Unlock() - - key := topoproto.TabletAliasString(alias) - count, ok := t.usedTablets[key] - if !ok { - panic(fmt.Sprintf("tablet: %v was never tracked", key)) - } - count-- - if count == 0 { - delete(t.usedTablets, key) - } else { - t.usedTablets[key] = count - } -} - -// TabletsInUse returns a string of all tablet aliases currently in use. -// The tablets are separated by a space. -func (t *TabletTracker) TabletsInUse() string { - t.mu.Lock() - defer t.mu.Unlock() - - var aliases []string - for alias := range t.usedTablets { - aliases = append(aliases, alias) - } - sort.Strings(aliases) - return strings.Join(aliases, " ") -} - -func (t *TabletTracker) tabletsByUsage() []string { - sorted := sortMapByValue(t.usedTablets) - var tablets []string - for _, pair := range sorted { - tablets = append(tablets, pair.Key) - } - return tablets -} - -// Sort by value was originally written by Andrew Gerrand: -// Source: https://groups.google.com/d/msg/golang-nuts/FT7cjmcL7gw/Gj4_aEsE_IsJ - -// Pair represents a tablet (Key) and its usage (Value). -type Pair struct { - Key string - Value int -} - -// PairList is a slice of Pairs that implements sort.Interface to sort by Value. -type PairList []Pair - -func (p PairList) Swap(i, j int) { p[i], p[j] = p[j], p[i] } -func (p PairList) Len() int { return len(p) } -func (p PairList) Less(i, j int) bool { return p[i].Value < p[j].Value } - -// A function to turn a map into a PairList, then sort and return it. -func sortMapByValue(m map[string]int) PairList { - p := make(PairList, len(m)) - i := 0 - for k, v := range m { - p[i] = Pair{k, v} - i++ - } - sort.Sort(p) - return p -} diff --git a/go/vt/worker/tablet_tracker_test.go b/go/vt/worker/tablet_tracker_test.go deleted file mode 100644 index 17375b6a11a..00000000000 --- a/go/vt/worker/tablet_tracker_test.go +++ /dev/null @@ -1,73 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package worker - -import ( - "testing" - - "google.golang.org/protobuf/proto" - - "vitess.io/vitess/go/vt/discovery" - "vitess.io/vitess/go/vt/topo" - - querypb "vitess.io/vitess/go/vt/proto/query" - topodatapb "vitess.io/vitess/go/vt/proto/topodata" -) - -var ts1 = discovery.LegacyTabletStats{ - Tablet: topo.NewTablet(10, "cell", "host1"), - Target: &querypb.Target{Keyspace: "k", Shard: "s", TabletType: topodatapb.TabletType_REPLICA}, -} -var ts2 = discovery.LegacyTabletStats{ - Tablet: topo.NewTablet(20, "cell", "host1"), - Target: &querypb.Target{Keyspace: "k", Shard: "s", TabletType: topodatapb.TabletType_REPLICA}, -} -var allTs = []discovery.LegacyTabletStats{ts1, ts2} - -func TestTabletsInUse(t *testing.T) { - tt := NewTabletTracker() - - tt.Track([]discovery.LegacyTabletStats{ts1}) - if got, want := tt.TabletsInUse(), "cell-0000000010"; got != want { - t.Fatalf("TabletsInUse() = %v, want = %v", got, want) - } - - tt.Track([]discovery.LegacyTabletStats{ts2}) - if got, want := tt.TabletsInUse(), "cell-0000000010 cell-0000000020"; got != want { - t.Fatalf("TabletsInUse() = %v, want = %v", got, want) - } -} - -func TestTrackUntrack(t *testing.T) { - tt := NewTabletTracker() - // ts1 will be used because no tablet is in use yet and ts1 is the first. - if got, want := tt.Track(allTs), ts1.Tablet; !proto.Equal(got, want) { - t.Fatalf("Track(%v) = %v, want = %v", allTs, got, want) - } - - // ts1 is already in use once, use ts2 now. - if got, want := tt.Track(allTs), ts2.Tablet; !proto.Equal(got, want) { - t.Fatalf("Track(%v) = %v, want = %v", allTs, got, want) - } - - // ts2 is no longer in use after Untrack(). - tt.Untrack(ts2.Tablet.Alias) - // ts2 instead of ts1 will be used because ts1 has a higher use count. - if got, want := tt.Track(allTs), ts2.Tablet; !proto.Equal(got, want) { - t.Fatalf("Track(%v) = %v, want = %v", allTs, got, want) - } -} diff --git a/go/vt/worker/topo_utils.go b/go/vt/worker/topo_utils.go deleted file mode 100644 index b707cf3955c..00000000000 --- a/go/vt/worker/topo_utils.go +++ /dev/null @@ -1,137 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package worker - -import ( - "flag" - "fmt" - "math/rand" - "time" - - "vitess.io/vitess/go/vt/vterrors" - - "context" - - "vitess.io/vitess/go/vt/discovery" - "vitess.io/vitess/go/vt/log" - "vitess.io/vitess/go/vt/topo/topoproto" - "vitess.io/vitess/go/vt/wrangler" - - topodatapb "vitess.io/vitess/go/vt/proto/topodata" -) - -var ( - // waitForHealthyTabletsTimeout intends to wait for the - // healthcheck to automatically return rdonly instances which - // have been taken out by previous *Clone or *Diff runs. - // Therefore, the default for this variable must be higher - // than vttablet's -health_check_interval. - waitForHealthyTabletsTimeout = flag.Duration("wait_for_healthy_tablets_timeout", 60*time.Second, "maximum time to wait at the start if less than --min_healthy_tablets are available") -) - -// FindHealthyTablet returns a random healthy tabletType tablet. -// Since we don't want to use them all, we require at least -// minHealthyRdonlyTablets servers to be healthy. -// May block up to -wait_for_healthy_rdonly_tablets_timeout. -func FindHealthyTablet(ctx context.Context, wr *wrangler.Wrangler, tsc *discovery.LegacyTabletStatsCache, cell, keyspace, shard string, minHealthyRdonlyTablets int, tabletType topodatapb.TabletType) (*topodatapb.TabletAlias, error) { - if tsc == nil { - // No healthcheck instance provided. Create one. - healthCheck := discovery.NewLegacyHealthCheck(*healthcheckRetryDelay, *healthCheckTimeout) - tsc = discovery.NewLegacyTabletStatsCache(healthCheck, wr.TopoServer(), cell) - watcher := discovery.NewLegacyShardReplicationWatcher(ctx, wr.TopoServer(), healthCheck, cell, keyspace, shard, *healthCheckTopologyRefresh, discovery.DefaultTopoReadConcurrency) - defer watcher.Stop() - defer healthCheck.Close() - } - - healthyTablets, err := waitForHealthyTablets(ctx, wr, tsc, cell, keyspace, shard, minHealthyRdonlyTablets, *waitForHealthyTabletsTimeout, tabletType) - if err != nil { - return nil, err - } - - // random server in the list is what we want - index := rand.Intn(len(healthyTablets)) - return healthyTablets[index].Tablet.Alias, nil -} - -func waitForHealthyTablets(ctx context.Context, wr *wrangler.Wrangler, tsc *discovery.LegacyTabletStatsCache, cell, keyspace, shard string, minHealthyRdonlyTablets int, timeout time.Duration, tabletType topodatapb.TabletType) ([]discovery.LegacyTabletStats, error) { - busywaitCtx, busywaitCancel := context.WithTimeout(ctx, timeout) - defer busywaitCancel() - - start := time.Now() - deadlineForLog, _ := busywaitCtx.Deadline() - log.V(2).Infof("Waiting for enough healthy %v tablets to become available in (%v,%v/%v). required: %v Waiting up to %.1f seconds.", tabletType, - cell, keyspace, shard, minHealthyRdonlyTablets, time.Until(deadlineForLog).Seconds()) - - // Wait for at least one RDONLY tablet initially before checking the list. - if err := tsc.WaitForTablets(busywaitCtx, keyspace, shard, tabletType); err != nil { - return nil, vterrors.Wrapf(err, "error waiting for %v tablets for (%v,%v/%v)", tabletType, cell, keyspace, shard) - } - - var healthyTablets []discovery.LegacyTabletStats - for { - select { - case <-busywaitCtx.Done(): - return nil, fmt.Errorf("not enough healthy %v tablets to choose from in (%v,%v/%v), have %v healthy ones, need at least %v Context error: %v", - tabletType, cell, keyspace, shard, len(healthyTablets), minHealthyRdonlyTablets, busywaitCtx.Err()) - default: - } - - healthyTablets = discovery.RemoveUnhealthyTablets(tsc.GetTabletStats(keyspace, shard, tabletType)) - if len(healthyTablets) >= minHealthyRdonlyTablets { - break - } - - deadlineForLog, _ := busywaitCtx.Deadline() - wr.Logger().Infof("Waiting for enough healthy %v tablets to become available (%v,%v/%v). available: %v required: %v Waiting up to %.1f more seconds.", - tabletType, cell, keyspace, shard, len(healthyTablets), minHealthyRdonlyTablets, time.Until(deadlineForLog).Seconds()) - // Block for 1 second because 2 seconds is the -health_check_interval flag value in integration tests. - timer := time.NewTimer(1 * time.Second) - select { - case <-busywaitCtx.Done(): - timer.Stop() - case <-timer.C: - } - } - log.V(2).Infof("At least %v healthy %v tablets are available in (%v,%v/%v) (required: %v). Took %.1f seconds to find this out.", - tabletType, len(healthyTablets), cell, keyspace, shard, minHealthyRdonlyTablets, time.Since(start).Seconds()) - return healthyTablets, nil -} - -// FindWorkerTablet will: -// - find a tabletType instance in the keyspace / shard -// - mark it as worker -// - tag it with our worker process -func FindWorkerTablet(ctx context.Context, wr *wrangler.Wrangler, cleaner *wrangler.Cleaner, tsc *discovery.LegacyTabletStatsCache, cell, keyspace, shard string, minHealthyTablets int, tabletType topodatapb.TabletType) (*topodatapb.TabletAlias, error) { - tabletAlias, err := FindHealthyTablet(ctx, wr, tsc, cell, keyspace, shard, minHealthyTablets, tabletType) - if err != nil { - return nil, err - } - - wr.Logger().Infof("Changing tablet %v to '%v'", topoproto.TabletAliasString(tabletAlias), topodatapb.TabletType_DRAINED) - shortCtx, cancel := context.WithTimeout(ctx, *remoteActionsTimeout) - defer cancel() - if err := wr.ChangeTabletType(shortCtx, tabletAlias, topodatapb.TabletType_DRAINED); err != nil { - return nil, err - } - // Record a clean-up action to take the tablet back to tabletAlias. - wrangler.RecordChangeTabletTypeAction(cleaner, tabletAlias, topodatapb.TabletType_DRAINED, tabletType) - return tabletAlias, nil -} - -func init() { - rand.Seed(time.Now().UnixNano()) -} diff --git a/go/vt/worker/utils_test.go b/go/vt/worker/utils_test.go deleted file mode 100644 index 966e27199ad..00000000000 --- a/go/vt/worker/utils_test.go +++ /dev/null @@ -1,111 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package worker - -import ( - "fmt" - "testing" - - "context" - - "vitess.io/vitess/go/mysql/fakesqldb" - "vitess.io/vitess/go/sqltypes" - "vitess.io/vitess/go/vt/topo" - "vitess.io/vitess/go/vt/vterrors" - "vitess.io/vitess/go/vt/vttablet/faketmclient" - "vitess.io/vitess/go/vt/vttablet/tmclient" - "vitess.io/vitess/go/vt/wrangler" - - querypb "vitess.io/vitess/go/vt/proto/query" - topodatapb "vitess.io/vitess/go/vt/proto/topodata" -) - -// This file contains common test helper. - -func runCommand(t *testing.T, wi *Instance, wr *wrangler.Wrangler, args []string) error { - // Limit the scope of the context e.g. to implicitly terminate stray Go - // routines. - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - worker, done, err := wi.RunCommand(ctx, args, wr, false /* runFromCli */) - if err != nil { - return vterrors.Wrap(err, "Worker creation failed") - } - if err := wi.WaitForCommand(worker, done); err != nil { - return vterrors.Wrap(err, "Worker failed") - } - - t.Logf("Got status: %v", worker.StatusAsText()) - if worker.State() != WorkerStateDone { - return vterrors.Wrap(err, "Worker finished but not successfully") - } - return nil -} - -// sourceRdonlyFakeDB fakes out the MIN, MAX query on the primary key. -// (This query is used to calculate the split points for reading a table -// using multiple threads.) -func sourceRdonlyFakeDB(t *testing.T, dbName, tableName string, min, max int) *fakesqldb.DB { - f := fakesqldb.New(t).OrderMatters() - f.AddExpectedExecuteFetch(fakesqldb.ExpectedExecuteFetch{ - Query: fmt.Sprintf("SELECT MIN(`id`), MAX(`id`) FROM `%s`.`%s`", dbName, tableName), - QueryResult: &sqltypes.Result{ - Fields: []*querypb.Field{ - { - Name: "min", - Type: sqltypes.Int64, - }, - { - Name: "max", - Type: sqltypes.Int64, - }, - }, - Rows: [][]sqltypes.Value{ - { - sqltypes.NewInt64(int64(min)), - sqltypes.NewInt64(int64(max)), - }, - }, - }, - }) - f.EnableInfinite() - return f -} - -// fakeTMCTopo is a FakeTabletManagerClient extension that implements ChangeType -// using the provided topo server. -type fakeTMCTopo struct { - tmclient.TabletManagerClient - server *topo.Server -} - -func newFakeTMCTopo(ts *topo.Server) tmclient.TabletManagerClient { - return &fakeTMCTopo{ - TabletManagerClient: faketmclient.NewFakeTabletManagerClient(), - server: ts, - } -} - -// ChangeType is part of the tmclient.TabletManagerClient interface. -func (f *fakeTMCTopo) ChangeType(ctx context.Context, tablet *topodatapb.Tablet, dbType topodatapb.TabletType, semiSync bool) error { - _, err := f.server.UpdateTabletFields(ctx, tablet.Alias, func(t *topodatapb.Tablet) error { - t.Type = dbType - return nil - }) - return err -} diff --git a/go/vt/worker/vertical_split_clone_cmd.go b/go/vt/worker/vertical_split_clone_cmd.go deleted file mode 100644 index bcb939268de..00000000000 --- a/go/vt/worker/vertical_split_clone_cmd.go +++ /dev/null @@ -1,325 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package worker - -import ( - "flag" - "fmt" - "html/template" - "net/http" - "strconv" - "strings" - "sync" - - "vitess.io/vitess/go/vt/proto/topodata" - "vitess.io/vitess/go/vt/vterrors" - - "context" - - "vitess.io/vitess/go/vt/concurrency" - "vitess.io/vitess/go/vt/topo/topoproto" - "vitess.io/vitess/go/vt/wrangler" -) - -const verticalSplitCloneHTML = ` - - - Vertical Split Clone Action - - -

    Vertical Split Clone Action

    - - {{if .Error}} - Error: {{.Error}}
    - {{else}} -

    Choose the destination keyspace for this action.

    -
      - {{range $i, $si := .Keyspaces}} -
    • {{$si}}
    • - {{end}} -
    - {{end}} - -` - -const verticalSplitCloneHTML2 = ` - - - Vertical Split Clone Action - - -

    Destination keyspace: {{.Keyspace}}

    -

    Vertical Split Clone Action

    -
    - -
    - -
    - -
    - -
    - -
    - -
    - -
    - -
    - -
    - - - -
    - -
    - -
    - - - ?
    - -
    - -` - -var verticalSplitCloneTemplate = mustParseTemplate("verticalSplitClone", verticalSplitCloneHTML) -var verticalSplitCloneTemplate2 = mustParseTemplate("verticalSplitClone2", verticalSplitCloneHTML2) - -func commandVerticalSplitClone(wi *Instance, wr *wrangler.Wrangler, subFlags *flag.FlagSet, args []string) (Worker, error) { - online := subFlags.Bool("online", defaultOnline, "do online copy (optional approximate copy, source and destination tablets will not be put out of serving, minimizes downtime during offline copy)") - offline := subFlags.Bool("offline", defaultOffline, "do offline copy (exact copy at a specific GTID, required before shard migration, source and destination tablets will be put out of serving during copy)") - tables := subFlags.String("tables", "", "comma separated list of tables to replicate (used for vertical split). Each is either an exact match, or a regular expression of the form /regexp/") - chunkCount := subFlags.Int("chunk_count", defaultChunkCount, "number of chunks per table") - minRowsPerChunk := subFlags.Int("min_rows_per_chunk", defaultMinRowsPerChunk, "minimum number of rows per chunk (may reduce --chunk_count)") - sourceReaderCount := subFlags.Int("source_reader_count", defaultSourceReaderCount, "number of concurrent streaming queries to use on the source") - writeQueryMaxRows := subFlags.Int("write_query_max_rows", defaultWriteQueryMaxRows, "maximum number of rows per write query") - writeQueryMaxSize := subFlags.Int("write_query_max_size", defaultWriteQueryMaxSize, "maximum size (in bytes) per write query") - destinationWriterCount := subFlags.Int("destination_writer_count", defaultDestinationWriterCount, "number of concurrent RPCs to execute on the destination") - minHealthyTablets := subFlags.Int("min_healthy_tablets", defaultMinHealthyTablets, "minimum number of healthy tablets before taking out one") - useConsistentSnapshot := subFlags.Bool("use_consistent_snapshot", defaultUseConsistentSnapshot, "Instead of pausing replication on the source, uses transactions with consistent snapshot to have a stable view of the data.") - tabletTypeStr := subFlags.String("tablet_type", "RDONLY", "tablet type to use (RDONLY or REPLICA)") - maxTPS := subFlags.Int64("max_tps", defaultMaxTPS, "if non-zero, limit copy to maximum number of (write) transactions/second on the destination (unlimited by default)") - maxReplicationLag := subFlags.Int64("max_replication_lag", defaultMaxReplicationLag, "if set, the adapative throttler will be enabled and automatically adjust the write rate to keep the lag below the set value in seconds (disabled by default)") - if err := subFlags.Parse(args); err != nil { - return nil, err - } - if subFlags.NArg() != 1 { - subFlags.Usage() - return nil, fmt.Errorf("command VerticalSplitClone requires ") - } - - keyspace, shard, err := topoproto.ParseKeyspaceShard(subFlags.Arg(0)) - if err != nil { - return nil, err - } - // The tableArray has zero elements when table flag is empty, which is an - // error case captured in function newVerticalSplitCloneWorker. - tableArray := []string{} - if *tables != "" { - tableArray = strings.Split(*tables, ",") - } - tabletType, ok := topodata.TabletType_value[*tabletTypeStr] - if !ok { - return nil, fmt.Errorf("command SplitClone invalid tablet_type: %v", tabletType) - } - - worker, err := newVerticalSplitCloneWorker(wr, wi.cell, keyspace, shard, *online, *offline, tableArray, *chunkCount, *minRowsPerChunk, *sourceReaderCount, *writeQueryMaxRows, *writeQueryMaxSize, *destinationWriterCount, *minHealthyTablets, topodata.TabletType(tabletType), *maxTPS, *maxReplicationLag, *useConsistentSnapshot) - if err != nil { - return nil, vterrors.Wrap(err, "cannot create worker") - } - return worker, nil -} - -// keyspacesWithServedFrom returns all the keyspaces that have ServedFrom set -// to one value. -func keyspacesWithServedFrom(ctx context.Context, wr *wrangler.Wrangler) ([]string, error) { - shortCtx, cancel := context.WithTimeout(ctx, *remoteActionsTimeout) - keyspaces, err := wr.TopoServer().GetKeyspaces(shortCtx) - cancel() - if err != nil { - return nil, vterrors.Wrap(err, "failed to get list of keyspaces") - } - - wg := sync.WaitGroup{} - mu := sync.Mutex{} // protects result - result := make([]string, 0, len(keyspaces)) - rec := concurrency.AllErrorRecorder{} - for _, keyspace := range keyspaces { - wg.Add(1) - go func(keyspace string) { - defer wg.Done() - shortCtx, cancel := context.WithTimeout(ctx, *remoteActionsTimeout) - ki, err := wr.TopoServer().GetKeyspace(shortCtx, keyspace) - cancel() - if err != nil { - rec.RecordError(vterrors.Wrapf(err, "failed to get details for keyspace '%v'", keyspace)) - return - } - if len(ki.ServedFroms) > 0 { - mu.Lock() - result = append(result, keyspace) - mu.Unlock() - } - }(keyspace) - } - wg.Wait() - - if rec.HasErrors() { - return nil, rec.Error() - } - if len(result) == 0 { - return nil, fmt.Errorf("there are no keyspaces with ServedFrom") - } - return result, nil -} - -func interactiveVerticalSplitClone(ctx context.Context, wi *Instance, wr *wrangler.Wrangler, w http.ResponseWriter, r *http.Request) (Worker, *template.Template, map[string]any, error) { - if err := r.ParseForm(); err != nil { - return nil, nil, nil, vterrors.Wrap(err, "cannot parse form") - } - - keyspace := r.FormValue("keyspace") - if keyspace == "" { - // display the list of possible keyspaces to choose from - result := make(map[string]any) - keyspaces, err := keyspacesWithServedFrom(ctx, wr) - if err != nil { - result["Error"] = err.Error() - } else { - result["Keyspaces"] = keyspaces - } - return nil, verticalSplitCloneTemplate, result, nil - } - - tables := r.FormValue("tables") - if tables == "" { - // display the input form - result := make(map[string]any) - result["Keyspace"] = keyspace - result["DefaultOnline"] = defaultOnline - result["DefaultOffline"] = defaultOffline - result["DefaultChunkCount"] = fmt.Sprintf("%v", defaultChunkCount) - result["DefaultMinRowsPerChunk"] = fmt.Sprintf("%v", defaultMinRowsPerChunk) - result["DefaultSourceReaderCount"] = fmt.Sprintf("%v", defaultSourceReaderCount) - result["DefaultWriteQueryMaxRows"] = fmt.Sprintf("%v", defaultWriteQueryMaxRows) - result["DefaultWriteQueryMaxSize"] = fmt.Sprintf("%v", defaultWriteQueryMaxSize) - result["DefaultDestinationWriterCount"] = fmt.Sprintf("%v", defaultDestinationWriterCount) - result["DefaultMinHealthyTablets"] = fmt.Sprintf("%v", defaultMinHealthyTablets) - result["DefaultMaxTPS"] = fmt.Sprintf("%v", defaultMaxTPS) - result["DefaultMaxReplicationLag"] = fmt.Sprintf("%v", defaultMaxReplicationLag) - result["DefaultUseConsistentSnapshot"] = fmt.Sprintf("%v", defaultUseConsistentSnapshot) - return nil, verticalSplitCloneTemplate2, result, nil - } - tableArray := strings.Split(tables, ",") - - // get other parameters - onlineStr := r.FormValue("online") - online := onlineStr == "true" - offlineStr := r.FormValue("offline") - offline := offlineStr == "true" - chunkCountStr := r.FormValue("chunkCount") - chunkCount, err := strconv.ParseInt(chunkCountStr, 0, 64) - if err != nil { - return nil, nil, nil, vterrors.Wrap(err, "cannot parse chunkCount") - } - minRowsPerChunkStr := r.FormValue("minRowsPerChunk") - minRowsPerChunk, err := strconv.ParseInt(minRowsPerChunkStr, 0, 64) - if err != nil { - return nil, nil, nil, vterrors.Wrap(err, "cannot parse minRowsPerChunk") - } - sourceReaderCountStr := r.FormValue("sourceReaderCount") - sourceReaderCount, err := strconv.ParseInt(sourceReaderCountStr, 0, 64) - if err != nil { - return nil, nil, nil, vterrors.Wrap(err, "cannot parse sourceReaderCount") - } - writeQueryMaxRowsStr := r.FormValue("writeQueryMaxRows") - writeQueryMaxRows, err := strconv.ParseInt(writeQueryMaxRowsStr, 0, 64) - if err != nil { - return nil, nil, nil, vterrors.Wrap(err, "cannot parse writeQueryMaxRows") - } - writeQueryMaxSizeStr := r.FormValue("writeQueryMaxSize") - writeQueryMaxSize, err := strconv.ParseInt(writeQueryMaxSizeStr, 0, 64) - if err != nil { - return nil, nil, nil, vterrors.Wrap(err, "cannot parse writeQueryMaxSize") - } - destinationWriterCountStr := r.FormValue("destinationWriterCount") - destinationWriterCount, err := strconv.ParseInt(destinationWriterCountStr, 0, 64) - if err != nil { - return nil, nil, nil, vterrors.Wrap(err, "cannot parse destinationWriterCount") - } - minHealthyTabletsStr := r.FormValue("minHealthyTablets") - minHealthyTablets, err := strconv.ParseInt(minHealthyTabletsStr, 0, 64) - if err != nil { - return nil, nil, nil, vterrors.Wrap(err, "cannot parse minHealthyTablets") - } - maxTPSStr := r.FormValue("maxTPS") - maxTPS, err := strconv.ParseInt(maxTPSStr, 0, 64) - if err != nil { - return nil, nil, nil, vterrors.Wrap(err, "cannot parse maxTPS") - } - maxReplicationLagStr := r.FormValue("maxReplicationLag") - maxReplicationLag, err := strconv.ParseInt(maxReplicationLagStr, 0, 64) - if err != nil { - return nil, nil, nil, vterrors.Wrap(err, "cannot parse maxReplicationLag") - } - tabletTypeStr := r.FormValue("tabletType") - tabletType, ok := topodata.TabletType_value[tabletTypeStr] - if !ok { - return nil, nil, nil, fmt.Errorf("cannot parse tabletType: %v", tabletType) - } - - // Figure out the shard - shortCtx, cancel := context.WithTimeout(ctx, *remoteActionsTimeout) - shardMap, err := wr.TopoServer().FindAllShardsInKeyspace(shortCtx, keyspace) - cancel() - if err != nil { - return nil, nil, nil, vterrors.Wrapf(err, "cannot find shard name for keyspace %s", keyspace) - } - if len(shardMap) != 1 { - return nil, nil, nil, fmt.Errorf("found the wrong number of shards, there should be only one, %v", shardMap) - } - var shard string - for s := range shardMap { - shard = s - } - - useConsistentSnapshotStr := r.FormValue("useConsistentSnapshot") - useConsistentSnapshot := useConsistentSnapshotStr == "true" - - // start the clone job - wrk, err := newVerticalSplitCloneWorker(wr, wi.cell, keyspace, shard, online, offline, tableArray, int(chunkCount), - int(minRowsPerChunk), int(sourceReaderCount), int(writeQueryMaxRows), int(writeQueryMaxSize), - int(destinationWriterCount), int(minHealthyTablets), topodata.TabletType(tabletType), maxTPS, - maxReplicationLag, useConsistentSnapshot) - if err != nil { - return nil, nil, nil, vterrors.Wrap(err, "cannot create worker") - } - return wrk, nil, nil, nil -} - -func init() { - AddCommand("Clones", Command{"VerticalSplitClone", - commandVerticalSplitClone, interactiveVerticalSplitClone, - "[--tables=''] ", - "Replicates the data and creates configuration for a vertical split."}) -} diff --git a/go/vt/worker/vertical_split_clone_test.go b/go/vt/worker/vertical_split_clone_test.go deleted file mode 100644 index ef309536539..00000000000 --- a/go/vt/worker/vertical_split_clone_test.go +++ /dev/null @@ -1,224 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package worker - -import ( - "context" - "testing" - "time" - - "vitess.io/vitess/go/mysql" - "vitess.io/vitess/go/mysql/fakesqldb" - "vitess.io/vitess/go/vt/discovery" - "vitess.io/vitess/go/vt/mysqlctl/tmutils" - "vitess.io/vitess/go/vt/topo/memorytopo" - "vitess.io/vitess/go/vt/topo/topoproto" - "vitess.io/vitess/go/vt/topotools" - "vitess.io/vitess/go/vt/vttablet/grpcqueryservice" - "vitess.io/vitess/go/vt/vttablet/queryservice/fakes" - "vitess.io/vitess/go/vt/wrangler/testlib" - - tabletmanagerdatapb "vitess.io/vitess/go/vt/proto/tabletmanagerdata" - topodatapb "vitess.io/vitess/go/vt/proto/topodata" -) - -const ( - // verticalSplitCloneTestMin is the minimum value of the primary key. - verticalSplitCloneTestMin int = 100 - // verticalSplitCloneTestMax is the maximum value of the primary key. - verticalSplitCloneTestMax int = 200 -) - -func createVerticalSplitCloneDestinationFakeDb(t *testing.T, name string, insertCount int) *fakesqldb.DB { - f := fakesqldb.New(t).SetName(name).OrderMatters() - - // Provoke a retry to test the error handling. (Let the first write fail.) - f.AddExpectedQuery("INSERT INTO `vt_destination_ks`.`moving1` (`id`, `msg`) VALUES (*", errReadOnly) - - for i := 1; i <= insertCount; i++ { - f.AddExpectedQuery("INSERT INTO `vt_destination_ks`.`moving1` (`id`, `msg`) VALUES (*", nil) - } - - return f -} - -// TestVerticalSplitClone will run VerticalSplitClone in the combined -// online and offline mode. The online phase will copy 100 rows from the source -// to the destination and the offline phase won't copy any rows as the source -// has not changed in the meantime. -func TestVerticalSplitClone(t *testing.T) { - delay := discovery.GetTabletPickerRetryDelay() - defer func() { - discovery.SetTabletPickerRetryDelay(delay) - }() - discovery.SetTabletPickerRetryDelay(5 * time.Millisecond) - - ts := memorytopo.NewServer("cell1", "cell2") - ctx := context.Background() - wi := NewInstance(ts, "cell1", time.Second) - - sourceRdonlyFakeDB := sourceRdonlyFakeDB(t, "vt_source_ks", "moving1", verticalSplitCloneTestMin, verticalSplitCloneTestMax) - - sourcePrimary := testlib.NewFakeTablet(t, wi.wr, "cell1", 0, - topodatapb.TabletType_PRIMARY, nil, testlib.TabletKeyspaceShard(t, "source_ks", "0")) - sourceRdonly := testlib.NewFakeTablet(t, wi.wr, "cell1", 1, - topodatapb.TabletType_RDONLY, sourceRdonlyFakeDB, testlib.TabletKeyspaceShard(t, "source_ks", "0")) - - // Create the destination keyspace with the appropriate ServedFromMap - ki := &topodatapb.Keyspace{ - ServedFroms: []*topodatapb.Keyspace_ServedFrom{ - { - TabletType: topodatapb.TabletType_PRIMARY, - Keyspace: "source_ks", - }, - { - TabletType: topodatapb.TabletType_REPLICA, - Keyspace: "source_ks", - }, - { - TabletType: topodatapb.TabletType_RDONLY, - Keyspace: "source_ks", - }, - }, - } - wi.wr.TopoServer().CreateKeyspace(ctx, "destination_ks", ki) - - // We read 100 source rows. sourceReaderCount is set to 10, so - // we'll have 100/10=10 rows per table chunk. - // destinationPackCount is set to 4, so we take 4 source rows - // at once. So we'll process 4 + 4 + 2 rows to get to 10. - // That means 3 insert statements on the target. So 3 * 10 - // = 30 insert statements on the destination. - destPrimaryFakeDb := createVerticalSplitCloneDestinationFakeDb(t, "destPrimary", 30) - defer destPrimaryFakeDb.VerifyAllExecutedOrFail() - - destPrimary := testlib.NewFakeTablet(t, wi.wr, "cell1", 10, - topodatapb.TabletType_PRIMARY, destPrimaryFakeDb, testlib.TabletKeyspaceShard(t, "destination_ks", "0")) - destRdonly := testlib.NewFakeTablet(t, wi.wr, "cell1", 11, - topodatapb.TabletType_RDONLY, nil, testlib.TabletKeyspaceShard(t, "destination_ks", "0")) - - // add the topo and schema data we'll need - if err := topotools.RebuildKeyspace(ctx, wi.wr.Logger(), wi.wr.TopoServer(), "source_ks", nil, false); err != nil { - t.Fatalf("RebuildKeyspaceGraph failed: %v", err) - } - if err := topotools.RebuildKeyspace(ctx, wi.wr.Logger(), wi.wr.TopoServer(), "destination_ks", nil, false); err != nil { - t.Fatalf("RebuildKeyspaceGraph failed: %v", err) - } - - // Set up source rdonly which will be used as input for the diff during the clone. - sourceRdonly.FakeMysqlDaemon.Schema = &tabletmanagerdatapb.SchemaDefinition{ - DatabaseSchema: "", - TableDefinitions: []*tabletmanagerdatapb.TableDefinition{ - { - Name: "moving1", - Columns: []string{"id", "msg"}, - PrimaryKeyColumns: []string{"id"}, - Type: tmutils.TableBaseTable, - // Set the row count to avoid that --min_rows_per_chunk reduces the - // number of chunks. - RowCount: 100, - }, - { - Name: "view1", - Type: tmutils.TableView, - }, - }, - } - sourceRdonly.FakeMysqlDaemon.CurrentPrimaryPosition = mysql.Position{ - GTIDSet: mysql.MariadbGTIDSet{12: mysql.MariadbGTID{Domain: 12, Server: 34, Sequence: 5678}}, - } - sourceRdonly.FakeMysqlDaemon.ExpectedExecuteSuperQueryList = []string{ - "STOP SLAVE", - "START SLAVE", - } - sourceRdonlyShqs := fakes.NewStreamHealthQueryService(sourceRdonly.Target()) - sourceRdonlyShqs.AddDefaultHealthResponse() - sourceRdonlyQs := newTestQueryService(t, sourceRdonly.Target(), sourceRdonlyShqs, 0, 1, topoproto.TabletAliasString(sourceRdonly.Tablet.Alias), true /* omitKeyspaceID */) - sourceRdonlyQs.addGeneratedRows(verticalSplitCloneTestMin, verticalSplitCloneTestMax) - grpcqueryservice.Register(sourceRdonly.RPCServer, sourceRdonlyQs) - - // Set up destination primary which will be used as input for the diff during the clone. - destPrimaryShqs := fakes.NewStreamHealthQueryService(destPrimary.Target()) - destPrimaryShqs.AddDefaultHealthResponse() - destPrimaryQs := newTestQueryService(t, destPrimary.Target(), destPrimaryShqs, 0, 1, topoproto.TabletAliasString(destPrimary.Tablet.Alias), true /* omitKeyspaceID */) - // This tablet is empty and does not return any rows. - grpcqueryservice.Register(destPrimary.RPCServer, destPrimaryQs) - - // Only wait 1 ms between retries, so that the test passes faster - *executeFetchRetryTime = (1 * time.Millisecond) - - // When the online clone inserted the last rows, modify the destination test - // query service such that it will return them as well. - destPrimaryFakeDb.GetEntry(29).AfterFunc = func() { - destPrimaryQs.addGeneratedRows(verticalSplitCloneTestMin, verticalSplitCloneTestMax) - } - - // Start action loop after having registered all RPC services. - for _, ft := range []*testlib.FakeTablet{sourcePrimary, sourceRdonly, destPrimary, destRdonly} { - ft.StartActionLoop(t, wi.wr) - defer ft.StopActionLoop(t) - } - - // Run the vtworker command. - args := []string{ - "VerticalSplitClone", - // --max_tps is only specified to enable the throttler and ensure that the - // code is executed. But the intent here is not to throttle the test, hence - // the rate limit is set very high. - "-max_tps", "9999", - "-tables", "/moving/,view1", - "-source_reader_count", "10", - // Each chunk pipeline will process 10 rows. To spread them out across 3 - // write queries, set the max row count per query to 4. (10 = 4+4+2) - "-write_query_max_rows", "4", - "-min_rows_per_chunk", "10", - "-destination_writer_count", "10", - // This test uses only one healthy RDONLY tablet. - "-min_healthy_tablets", "1", - "destination_ks/0", - } - if err := runCommand(t, wi, wi.wr, args); err != nil { - t.Fatal(err) - } - if inserts := statsOnlineInsertsCounters.Counts()["moving1"]; inserts != 100 { - t.Errorf("wrong number of rows inserted: got = %v, want = %v", inserts, 100) - } - if updates := statsOnlineUpdatesCounters.Counts()["moving1"]; updates != 0 { - t.Errorf("wrong number of rows updated: got = %v, want = %v", updates, 0) - } - if deletes := statsOnlineDeletesCounters.Counts()["moving1"]; deletes != 0 { - t.Errorf("wrong number of rows deleted: got = %v, want = %v", deletes, 0) - } - if inserts := statsOfflineInsertsCounters.Counts()["moving1"]; inserts != 0 { - t.Errorf("no stats for the offline clone phase should have been modified. got inserts = %v", inserts) - } - if updates := statsOfflineUpdatesCounters.Counts()["moving1"]; updates != 0 { - t.Errorf("no stats for the offline clone phase should have been modified. got updates = %v", updates) - } - if deletes := statsOfflineDeletesCounters.Counts()["moving1"]; deletes != 0 { - t.Errorf("no stats for the offline clone phase should have been modified. got deletes = %v", deletes) - } - - wantRetryCount := int64(1) - if got := statsRetryCount.Get(); got != wantRetryCount { - t.Errorf("Wrong statsRetryCounter: got %v, wanted %v", got, wantRetryCount) - } - wantRetryReadOnlyCount := int64(1) - if got := statsRetryCounters.Counts()[retryCategoryReadOnly]; got != wantRetryReadOnlyCount { - t.Errorf("Wrong statsRetryCounters: got %v, wanted %v", got, wantRetryReadOnlyCount) - } -} diff --git a/go/vt/worker/vertical_split_diff.go b/go/vt/worker/vertical_split_diff.go deleted file mode 100644 index 8785bfd6915..00000000000 --- a/go/vt/worker/vertical_split_diff.go +++ /dev/null @@ -1,467 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package worker - -import ( - "context" - "fmt" - "html/template" - "sync" - - "vitess.io/vitess/go/sqltypes" - "vitess.io/vitess/go/sync2" - "vitess.io/vitess/go/vt/binlog/binlogplayer" - "vitess.io/vitess/go/vt/concurrency" - "vitess.io/vitess/go/vt/mysqlctl/tmutils" - "vitess.io/vitess/go/vt/topo" - "vitess.io/vitess/go/vt/topo/topoproto" - "vitess.io/vitess/go/vt/vtctl/schematools" - "vitess.io/vitess/go/vt/vterrors" - "vitess.io/vitess/go/vt/wrangler" - - tabletmanagerdatapb "vitess.io/vitess/go/vt/proto/tabletmanagerdata" - topodatapb "vitess.io/vitess/go/vt/proto/topodata" -) - -// VerticalSplitDiffWorker executes a diff between a destination shard and its -// source shards in a shard split case. -type VerticalSplitDiffWorker struct { - StatusWorker - - wr *wrangler.Wrangler - cell string - keyspace string - shard string - minHealthyRdonlyTablets int - parallelDiffsCount int - cleaner *wrangler.Cleaner - - // populated during WorkerStateInit, read-only after that - keyspaceInfo *topo.KeyspaceInfo - shardInfo *topo.ShardInfo - - // populated during WorkerStateFindTargets, read-only after that - sourceAlias *topodatapb.TabletAlias - destinationAlias *topodatapb.TabletAlias - destinationTabletType topodatapb.TabletType - - // populated during WorkerStateDiff - sourceSchemaDefinition *tabletmanagerdatapb.SchemaDefinition - destinationSchemaDefinition *tabletmanagerdatapb.SchemaDefinition -} - -// NewVerticalSplitDiffWorker returns a new VerticalSplitDiffWorker object. -func NewVerticalSplitDiffWorker(wr *wrangler.Wrangler, cell, keyspace, shard string, minHealthyRdonlyTablets, parallelDiffsCount int, destintationTabletType topodatapb.TabletType) Worker { - return &VerticalSplitDiffWorker{ - StatusWorker: NewStatusWorker(), - wr: wr, - cell: cell, - keyspace: keyspace, - shard: shard, - minHealthyRdonlyTablets: minHealthyRdonlyTablets, - destinationTabletType: destintationTabletType, - parallelDiffsCount: parallelDiffsCount, - cleaner: &wrangler.Cleaner{}, - } -} - -// StatusAsHTML is part of the Worker interface. -func (vsdw *VerticalSplitDiffWorker) StatusAsHTML() template.HTML { - state := vsdw.State() - - result := "Working on: " + vsdw.keyspace + "/" + vsdw.shard + "
    \n" - result += "State: " + state.String() + "
    \n" - switch state { - case WorkerStateDiff: - result += "Running:
    \n" - case WorkerStateDiffWillFail: - result += "Running - have already found differences...
    \n" - case WorkerStateDone: - result += "Success:
    \n" - } - - return template.HTML(result) -} - -// StatusAsText is part of the Worker interface. -func (vsdw *VerticalSplitDiffWorker) StatusAsText() string { - state := vsdw.State() - - result := "Working on: " + vsdw.keyspace + "/" + vsdw.shard + "\n" - result += "State: " + state.String() + "\n" - switch state { - case WorkerStateDiff: - result += "Running...\n" - case WorkerStateDiffWillFail: - result += "Running - have already found differences...\n" - case WorkerStateDone: - result += "Success.\n" - } - return result -} - -// Run is mostly a wrapper to run the cleanup at the end. -func (vsdw *VerticalSplitDiffWorker) Run(ctx context.Context) error { - resetVars() - err := vsdw.run(ctx) - - vsdw.SetState(WorkerStateCleanUp) - cerr := vsdw.cleaner.CleanUp(vsdw.wr) - if cerr != nil { - if err != nil { - vsdw.wr.Logger().Errorf2(cerr, "CleanUp failed in addition to job error") - } else { - err = cerr - } - } - if err != nil { - vsdw.SetState(WorkerStateError) - return err - } - vsdw.SetState(WorkerStateDone) - return nil -} - -func (vsdw *VerticalSplitDiffWorker) run(ctx context.Context) error { - // first state: read what we need to do - if err := vsdw.init(ctx); err != nil { - return vterrors.Wrap(err, "init() failed") - } - if err := checkDone(ctx); err != nil { - return err - } - - // second state: find targets - if err := vsdw.findTargets(ctx); err != nil { - return vterrors.Wrap(err, "findTargets() failed") - } - if err := checkDone(ctx); err != nil { - return err - } - - // third phase: synchronize replication - if err := vsdw.synchronizeReplication(ctx); err != nil { - return vterrors.Wrap(err, "synchronizeReplication() failed") - } - if err := checkDone(ctx); err != nil { - return err - } - - // fourth phase: diff - if err := vsdw.diff(ctx); err != nil { - return vterrors.Wrap(err, "diff() failed") - } - if err := checkDone(ctx); err != nil { - return err - } - - return nil -} - -// init phase: -// - read the shard info, make sure it has sources -func (vsdw *VerticalSplitDiffWorker) init(ctx context.Context) error { - vsdw.SetState(WorkerStateInit) - - var err error - - // read the keyspace and validate it - vsdw.keyspaceInfo, err = vsdw.wr.TopoServer().GetKeyspace(ctx, vsdw.keyspace) - if err != nil { - return vterrors.Wrapf(err, "cannot read keyspace %v", vsdw.keyspace) - } - if len(vsdw.keyspaceInfo.ServedFroms) == 0 { - return fmt.Errorf("keyspace %v has no KeyspaceServedFrom", vsdw.keyspace) - } - - // read the shardinfo and validate it - vsdw.shardInfo, err = vsdw.wr.TopoServer().GetShard(ctx, vsdw.keyspace, vsdw.shard) - if err != nil { - return vterrors.Wrapf(err, "cannot read shard %v/%v", vsdw.keyspace, vsdw.shard) - } - if len(vsdw.shardInfo.SourceShards) != 1 { - return fmt.Errorf("shard %v/%v has bad number of source shards", vsdw.keyspace, vsdw.shard) - } - if len(vsdw.shardInfo.SourceShards[0].Tables) == 0 { - return fmt.Errorf("shard %v/%v has no tables in source shard[0]", vsdw.keyspace, vsdw.shard) - } - if !vsdw.shardInfo.HasPrimary() { - return fmt.Errorf("shard %v/%v has no master", vsdw.keyspace, vsdw.shard) - } - - return nil -} - -// findTargets phase: -// - find one destinationTabletType in destination shard -// - find one rdonly per source shard -// - mark them all as 'worker' pointing back to us -func (vsdw *VerticalSplitDiffWorker) findTargets(ctx context.Context) error { - vsdw.SetState(WorkerStateFindTargets) - - // find an appropriate tablet in destination shard - var err error - vsdw.destinationAlias, err = FindWorkerTablet( - ctx, - vsdw.wr, - vsdw.cleaner, - nil, /* tsc */ - vsdw.cell, - vsdw.keyspace, - vsdw.shard, - 1, /* minHealthyTablets */ - vsdw.destinationTabletType, - ) - if err != nil { - return vterrors.Wrapf(err, "FindWorkerTablet() failed for %v/%v/%v", vsdw.cell, vsdw.keyspace, vsdw.shard) - } - - // find an appropriate tablet in the source shard - vsdw.sourceAlias, err = FindWorkerTablet(ctx, vsdw.wr, vsdw.cleaner, nil /* tsc */, vsdw.cell, vsdw.shardInfo.SourceShards[0].Keyspace, vsdw.shardInfo.SourceShards[0].Shard, vsdw.minHealthyRdonlyTablets, topodatapb.TabletType_RDONLY) - if err != nil { - return vterrors.Wrapf(err, "FindWorkerTablet() failed for %v/%v/%v", vsdw.cell, vsdw.shardInfo.SourceShards[0].Keyspace, vsdw.shardInfo.SourceShards[0].Shard) - } - - return nil -} - -// synchronizeReplication phase: -// 1 - ask the primary of the destination shard to pause filtered replication, -// and return the source binlog positions -// (add a cleanup task to restart filtered replication on primary) -// 2 - stop the source tablet at a binlog position higher than the -// destination primary. Get that new position. -// (add a cleanup task to restart binlog replication on it, and change -// the existing ChangeTabletType cleanup action to 'spare' type) -// 3 - ask the primary of the destination shard to resume filtered replication -// up to the new list of positions, and return its binlog position. -// 4 - wait until the destination tablet is equal or passed that primary -// binlog position, and stop its replication. -// (add a cleanup task to restart binlog replication on it, and change -// the existing ChangeTabletType cleanup action to 'spare' type) -// 5 - restart filtered replication on destination primary. -// (remove the cleanup task that does the same) -// At this point, all source and destination tablets are stopped at the same point. - -func (vsdw *VerticalSplitDiffWorker) synchronizeReplication(ctx context.Context) error { - vsdw.SetState(WorkerStateSyncReplication) - - shortCtx, cancel := context.WithTimeout(ctx, *remoteActionsTimeout) - defer cancel() - masterInfo, err := vsdw.wr.TopoServer().GetTablet(shortCtx, vsdw.shardInfo.PrimaryAlias) - if err != nil { - return vterrors.Wrapf(err, "synchronizeReplication: cannot get Tablet record for master %v", topoproto.TabletAliasString(vsdw.shardInfo.PrimaryAlias)) - } - - ss := vsdw.shardInfo.SourceShards[0] - - // 1 - stop the primary binlog replication, get its current position - vsdw.wr.Logger().Infof("Stopping master binlog replication on %v", topoproto.TabletAliasString(vsdw.shardInfo.PrimaryAlias)) - shortCtx, cancel = context.WithTimeout(ctx, *remoteActionsTimeout) - defer cancel() - _, err = vsdw.wr.TabletManagerClient().VReplicationExec(shortCtx, masterInfo.Tablet, binlogplayer.StopVReplication(ss.Uid, "for split diff")) - if err != nil { - return vterrors.Wrapf(err, "Stop VReplication on master %v failed", topoproto.TabletAliasString(vsdw.shardInfo.PrimaryAlias)) - } - wrangler.RecordVReplicationAction(vsdw.cleaner, masterInfo.Tablet, binlogplayer.StartVReplication(ss.Uid)) - p3qr, err := vsdw.wr.TabletManagerClient().VReplicationExec(shortCtx, masterInfo.Tablet, binlogplayer.ReadVReplicationPos(ss.Uid)) - if err != nil { - return vterrors.Wrapf(err, "VReplicationExec(stop) for %v failed", vsdw.shardInfo.PrimaryAlias) - } - qr := sqltypes.Proto3ToResult(p3qr) - if len(qr.Rows) != 1 || len(qr.Rows[0]) != 1 { - return fmt.Errorf("unexpected result while reading position: %v", qr) - } - vreplicationPos := qr.Rows[0][0].ToString() - - // stop replication - vsdw.wr.Logger().Infof("Stopping replication %v at a minimum of %v", topoproto.TabletAliasString(vsdw.sourceAlias), vreplicationPos) - shortCtx, cancel = context.WithTimeout(ctx, *remoteActionsTimeout) - defer cancel() - sourceTablet, err := vsdw.wr.TopoServer().GetTablet(shortCtx, vsdw.sourceAlias) - if err != nil { - return err - } - mysqlPos, err := vsdw.wr.TabletManagerClient().StopReplicationMinimum(shortCtx, sourceTablet.Tablet, vreplicationPos, *remoteActionsTimeout) - if err != nil { - return vterrors.Wrapf(err, "cannot stop replica %v at right binlog position %v", topoproto.TabletAliasString(vsdw.sourceAlias), vreplicationPos) - } - - // change the cleaner actions from ChangeTabletType(rdonly) - // to StartReplication() + ChangeTabletType(spare) - wrangler.RecordStartReplicationAction(vsdw.cleaner, sourceTablet.Tablet) - - // 3 - ask the primary of the destination shard to resume filtered - // replication up to the new list of positions - vsdw.wr.Logger().Infof("Restarting master %v until it catches up to %v", topoproto.TabletAliasString(vsdw.shardInfo.PrimaryAlias), mysqlPos) - shortCtx, cancel = context.WithTimeout(ctx, *remoteActionsTimeout) - defer cancel() - _, err = vsdw.wr.TabletManagerClient().VReplicationExec(shortCtx, masterInfo.Tablet, binlogplayer.StartVReplicationUntil(ss.Uid, mysqlPos)) - if err != nil { - return vterrors.Wrapf(err, "VReplication(start until) for %v until %v failed", vsdw.shardInfo.PrimaryAlias, mysqlPos) - } - if err := vsdw.wr.TabletManagerClient().VReplicationWaitForPos(shortCtx, masterInfo.Tablet, int(ss.Uid), mysqlPos); err != nil { - return vterrors.Wrapf(err, "VReplicationWaitForPos for %v until %v failed", vsdw.shardInfo.PrimaryAlias, mysqlPos) - } - masterPos, err := vsdw.wr.TabletManagerClient().PrimaryPosition(shortCtx, masterInfo.Tablet) - if err != nil { - return vterrors.Wrapf(err, "PrimaryPosition for %v failed", vsdw.shardInfo.PrimaryAlias) - } - - // 4 - wait until the destination tablet is equal or passed - // that primary binlog position, and stop its replication. - vsdw.wr.Logger().Infof("Waiting for destination tablet %v to catch up to %v", topoproto.TabletAliasString(vsdw.destinationAlias), masterPos) - shortCtx, cancel = context.WithTimeout(ctx, *remoteActionsTimeout) - defer cancel() - destinationTablet, err := vsdw.wr.TopoServer().GetTablet(shortCtx, vsdw.destinationAlias) - if err != nil { - return err - } - shortCtx, cancel = context.WithTimeout(ctx, *remoteActionsTimeout) - defer cancel() - _, err = vsdw.wr.TabletManagerClient().StopReplicationMinimum(shortCtx, destinationTablet.Tablet, masterPos, *remoteActionsTimeout) - if err != nil { - return vterrors.Wrapf(err, "StopReplicationMinimum on %v at %v failed", topoproto.TabletAliasString(vsdw.destinationAlias), masterPos) - } - wrangler.RecordStartReplicationAction(vsdw.cleaner, destinationTablet.Tablet) - - // 5 - restart filtered replication on destination primary - vsdw.wr.Logger().Infof("Restarting filtered replication on master %v", topoproto.TabletAliasString(vsdw.shardInfo.PrimaryAlias)) - shortCtx, cancel = context.WithTimeout(ctx, *remoteActionsTimeout) - defer cancel() - if _, err = vsdw.wr.TabletManagerClient().VReplicationExec(shortCtx, masterInfo.Tablet, binlogplayer.StartVReplication(ss.Uid)); err != nil { - return vterrors.Wrapf(err, "VReplicationExec(start) failed for %v", vsdw.shardInfo.PrimaryAlias) - } - - return nil -} - -// diff phase: will create a list of messages regarding the diff. -// - get the schema on all tablets -// - if some table schema mismatches, record them (use existing schema diff tools). -// - for each table in destination, run a diff pipeline. - -func (vsdw *VerticalSplitDiffWorker) diff(ctx context.Context) error { - vsdw.SetState(WorkerStateDiff) - - vsdw.wr.Logger().Infof("Gathering schema information...") - wg := sync.WaitGroup{} - rec := &concurrency.AllErrorRecorder{} - wg.Add(1) - go func() { - var err error - shortCtx, cancel := context.WithTimeout(ctx, *remoteActionsTimeout) - req := &tabletmanagerdatapb.GetSchemaRequest{Tables: vsdw.shardInfo.SourceShards[0].Tables} - vsdw.destinationSchemaDefinition, err = schematools.GetSchema( - shortCtx, vsdw.wr.TopoServer(), vsdw.wr.TabletManagerClient(), vsdw.destinationAlias, req) - cancel() - if err != nil { - vsdw.markAsWillFail(rec, err) - } - vsdw.wr.Logger().Infof("Got schema from destination %v", topoproto.TabletAliasString(vsdw.destinationAlias)) - wg.Done() - }() - wg.Add(1) - go func() { - var err error - shortCtx, cancel := context.WithTimeout(ctx, *remoteActionsTimeout) - req := &tabletmanagerdatapb.GetSchemaRequest{Tables: vsdw.shardInfo.SourceShards[0].Tables} - vsdw.sourceSchemaDefinition, err = schematools.GetSchema( - shortCtx, vsdw.wr.TopoServer(), vsdw.wr.TabletManagerClient(), vsdw.sourceAlias, req) - cancel() - if err != nil { - vsdw.markAsWillFail(rec, err) - } - vsdw.wr.Logger().Infof("Got schema from source %v", topoproto.TabletAliasString(vsdw.sourceAlias)) - wg.Done() - }() - wg.Wait() - if rec.HasErrors() { - return rec.Error() - } - - // Check the schema - vsdw.wr.Logger().Infof("Diffing the schema...") - rec = &concurrency.AllErrorRecorder{} - tmutils.DiffSchema("destination", vsdw.destinationSchemaDefinition, "source", vsdw.sourceSchemaDefinition, rec) - if rec.HasErrors() { - vsdw.wr.Logger().Warningf("Different schemas: %v", rec.Error()) - } else { - vsdw.wr.Logger().Infof("Schema match, good.") - } - - // run the diffs, 8 at a time - vsdw.wr.Logger().Infof("Running the diffs...") - sem := sync2.NewSemaphore(vsdw.parallelDiffsCount, 0) - for _, tableDefinition := range vsdw.destinationSchemaDefinition.TableDefinitions { - wg.Add(1) - go func(tableDefinition *tabletmanagerdatapb.TableDefinition) { - defer wg.Done() - sem.Acquire() - defer sem.Release() - - vsdw.wr.Logger().Infof("Starting the diff on table %v", tableDefinition.Name) - sourceQueryResultReader, err := TableScan(ctx, vsdw.wr.Logger(), vsdw.wr.TopoServer(), vsdw.sourceAlias, tableDefinition) - if err != nil { - newErr := vterrors.Wrap(err, "TableScan(source) failed") - vsdw.markAsWillFail(rec, newErr) - vsdw.wr.Logger().Error(newErr) - return - } - defer sourceQueryResultReader.Close(ctx) - - destinationQueryResultReader, err := TableScan(ctx, vsdw.wr.Logger(), vsdw.wr.TopoServer(), vsdw.destinationAlias, tableDefinition) - if err != nil { - newErr := vterrors.Wrap(err, "TableScan(destination) failed") - vsdw.markAsWillFail(rec, newErr) - vsdw.wr.Logger().Error(newErr) - return - } - defer destinationQueryResultReader.Close(ctx) - - differ, err := NewRowDiffer(sourceQueryResultReader, destinationQueryResultReader, tableDefinition) - if err != nil { - newErr := vterrors.Wrap(err, "NewRowDiffer() failed") - vsdw.markAsWillFail(rec, newErr) - vsdw.wr.Logger().Error(newErr) - return - } - - report, err := differ.Go(vsdw.wr.Logger()) - if err != nil { - vsdw.wr.Logger().Errorf2(err, "Differ.Go failed") - } else { - if report.HasDifferences() { - err := fmt.Errorf("table %v has differences: %v", tableDefinition.Name, report.String()) - vsdw.markAsWillFail(rec, err) - vsdw.wr.Logger().Error(err) - } else { - vsdw.wr.Logger().Infof("Table %v checks out (%v rows processed, %v qps)", tableDefinition.Name, report.processedRows, report.processingQPS) - } - } - }(tableDefinition) - } - wg.Wait() - - return rec.Error() -} - -// markAsWillFail records the error and changes the state of the worker to reflect this -func (vsdw *VerticalSplitDiffWorker) markAsWillFail(er concurrency.ErrorRecorder, err error) { - er.RecordError(err) - vsdw.SetState(WorkerStateDiffWillFail) -} diff --git a/go/vt/worker/vertical_split_diff_cmd.go b/go/vt/worker/vertical_split_diff_cmd.go deleted file mode 100644 index 5d6cac7260f..00000000000 --- a/go/vt/worker/vertical_split_diff_cmd.go +++ /dev/null @@ -1,216 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package worker - -import ( - "flag" - "fmt" - "html/template" - "net/http" - "strconv" - "sync" - - "vitess.io/vitess/go/vt/vterrors" - - "context" - - "vitess.io/vitess/go/vt/concurrency" - "vitess.io/vitess/go/vt/topo/topoproto" - "vitess.io/vitess/go/vt/wrangler" - - topodatapb "vitess.io/vitess/go/vt/proto/topodata" -) - -const verticalSplitDiffHTML = ` - - - Vertical Split Diff Action - - -

    Vertical Split Diff Action

    - - {{if .Error}} - Error: {{.Error}}
    - {{else}} - {{range $i, $si := .Shards}} -
  • {{$si.Keyspace}}/{{$si.Shard}}
  • - {{end}} - {{end}} - -` -const verticalSplitDiffHTML2 = ` - - - Vertical Split Diff Action - - -

    Shard involved: {{.Keyspace}}/{{.Shard}}

    -

    Vertical Split Diff Action

    -
    - - -
    - -
    - - -
    - -` - -var verticalSplitDiffTemplate = mustParseTemplate("verticalSplitDiff", verticalSplitDiffHTML) -var verticalSplitDiffTemplate2 = mustParseTemplate("verticalSplitDiff2", verticalSplitDiffHTML2) - -func commandVerticalSplitDiff(wi *Instance, wr *wrangler.Wrangler, subFlags *flag.FlagSet, args []string) (Worker, error) { - minHealthyRdonlyTablets := subFlags.Int("min_healthy_rdonly_tablets", defaultMinHealthyTablets, "minimum number of healthy RDONLY tablets before taking out one") - parallelDiffsCount := subFlags.Int("parallel_diffs_count", defaultParallelDiffsCount, "number of tables to diff in parallel") - destTabletTypeStr := subFlags.String("dest_tablet_type", defaultDestTabletType, "destination tablet type (RDONLY or REPLICA) that will be used to compare the shards") - if err := subFlags.Parse(args); err != nil { - return nil, err - } - if subFlags.NArg() != 1 { - subFlags.Usage() - return nil, fmt.Errorf("command VerticalSplitDiff requires ") - } - keyspace, shard, err := topoproto.ParseKeyspaceShard(subFlags.Arg(0)) - if err != nil { - return nil, err - } - - destTabletType, ok := topodatapb.TabletType_value[*destTabletTypeStr] - if !ok { - return nil, fmt.Errorf("command VerticalSplitDiff invalid dest_tablet_type: %v", destTabletType) - } - - return NewVerticalSplitDiffWorker(wr, wi.cell, keyspace, shard, *minHealthyRdonlyTablets, *parallelDiffsCount, topodatapb.TabletType(destTabletType)), nil -} - -// shardsWithTablesSources returns all the shards that have SourceShards set -// to one value, with an array of Tables. -func shardsWithTablesSources(ctx context.Context, wr *wrangler.Wrangler) ([]map[string]string, error) { - shortCtx, cancel := context.WithTimeout(ctx, *remoteActionsTimeout) - keyspaces, err := wr.TopoServer().GetKeyspaces(shortCtx) - cancel() - if err != nil { - return nil, vterrors.Wrap(err, "failed to get list of keyspaces") - } - - wg := sync.WaitGroup{} - mu := sync.Mutex{} // protects result - result := make([]map[string]string, 0, len(keyspaces)) - rec := concurrency.AllErrorRecorder{} - for _, keyspace := range keyspaces { - wg.Add(1) - go func(keyspace string) { - defer wg.Done() - shortCtx, cancel := context.WithTimeout(ctx, *remoteActionsTimeout) - shards, err := wr.TopoServer().GetShardNames(shortCtx, keyspace) - cancel() - if err != nil { - rec.RecordError(vterrors.Wrapf(err, "failed to get list of shards for keyspace '%v'", keyspace)) - return - } - for _, shard := range shards { - wg.Add(1) - go func(keyspace, shard string) { - defer wg.Done() - shortCtx, cancel := context.WithTimeout(ctx, *remoteActionsTimeout) - si, err := wr.TopoServer().GetShard(shortCtx, keyspace, shard) - cancel() - if err != nil { - rec.RecordError(vterrors.Wrapf(err, "failed to get details for shard '%v'", topoproto.KeyspaceShardString(keyspace, shard))) - return - } - - if len(si.SourceShards) == 1 && len(si.SourceShards[0].Tables) > 0 { - mu.Lock() - result = append(result, map[string]string{ - "Keyspace": keyspace, - "Shard": shard, - }) - mu.Unlock() - } - }(keyspace, shard) - } - }(keyspace) - } - wg.Wait() - - if rec.HasErrors() { - return nil, rec.Error() - } - if len(result) == 0 { - return nil, fmt.Errorf("there are no shards with SourceShards") - } - return result, nil -} - -func interactiveVerticalSplitDiff(ctx context.Context, wi *Instance, wr *wrangler.Wrangler, w http.ResponseWriter, r *http.Request) (Worker, *template.Template, map[string]any, error) { - if err := r.ParseForm(); err != nil { - return nil, nil, nil, vterrors.Wrap(err, "cannot parse form") - } - keyspace := r.FormValue("keyspace") - shard := r.FormValue("shard") - - if keyspace == "" || shard == "" { - // display the list of possible shards to chose from - result := make(map[string]any) - shards, err := shardsWithTablesSources(ctx, wr) - if err != nil { - result["Error"] = err.Error() - } else { - result["Shards"] = shards - } - return nil, verticalSplitDiffTemplate, result, nil - } - - submitButtonValue := r.FormValue("submit") - if submitButtonValue == "" { - // display the input form - result := make(map[string]any) - result["Keyspace"] = keyspace - result["Shard"] = shard - result["DefaultMinHealthyRdonlyTablets"] = fmt.Sprintf("%v", defaultMinHealthyTablets) - result["DefaultParallelDiffsCount"] = fmt.Sprintf("%v", defaultParallelDiffsCount) - return nil, verticalSplitDiffTemplate2, result, nil - } - - // get other parameters - minHealthyRdonlyTabletsStr := r.FormValue("minHealthyRdonlyTablets") - minHealthyRdonlyTablets, err := strconv.ParseInt(minHealthyRdonlyTabletsStr, 0, 64) - if err != nil { - return nil, nil, nil, vterrors.Wrap(err, "cannot parse minHealthyRdonlyTablets") - } - parallelDiffsCountStr := r.FormValue("parallelDiffsCount") - parallelDiffsCount, err := strconv.ParseInt(parallelDiffsCountStr, 0, 64) - if err != nil { - return nil, nil, nil, vterrors.Wrap(err, "cannot parse parallelDiffsCount") - } - - // start the diff job - // TODO: @rafael - Add option to set destination tablet type in UI form. - wrk := NewVerticalSplitDiffWorker(wr, wi.cell, keyspace, shard, int(minHealthyRdonlyTablets), int(parallelDiffsCount), topodatapb.TabletType_RDONLY) - return wrk, nil, nil, nil -} - -func init() { - AddCommand("Diffs", Command{"VerticalSplitDiff", - commandVerticalSplitDiff, interactiveVerticalSplitDiff, - "", - "Diffs an rdonly tablet from the (destination) keyspace/shard against an rdonly tablet from the respective source keyspace/shard." + - " Only compares the tables which were set by a previous VerticalSplitClone command."}) -} diff --git a/go/vt/worker/vertical_split_diff_test.go b/go/vt/worker/vertical_split_diff_test.go deleted file mode 100644 index bbfd127e0e0..00000000000 --- a/go/vt/worker/vertical_split_diff_test.go +++ /dev/null @@ -1,202 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package worker - -import ( - "context" - "fmt" - "strings" - "testing" - "time" - - "vitess.io/vitess/go/sqltypes" - "vitess.io/vitess/go/vt/discovery" - "vitess.io/vitess/go/vt/logutil" - "vitess.io/vitess/go/vt/mysqlctl/tmutils" - "vitess.io/vitess/go/vt/topo/memorytopo" - "vitess.io/vitess/go/vt/topotools" - "vitess.io/vitess/go/vt/vttablet/grpcqueryservice" - "vitess.io/vitess/go/vt/vttablet/queryservice/fakes" - "vitess.io/vitess/go/vt/wrangler" - "vitess.io/vitess/go/vt/wrangler/testlib" - - querypb "vitess.io/vitess/go/vt/proto/query" - tabletmanagerdatapb "vitess.io/vitess/go/vt/proto/tabletmanagerdata" - topodatapb "vitess.io/vitess/go/vt/proto/topodata" -) - -// verticalDiffTabletServer is a local QueryService implementation to -// support the tests -type verticalDiffTabletServer struct { - t *testing.T - - *fakes.StreamHealthQueryService -} - -func (sq *verticalDiffTabletServer) StreamExecute(ctx context.Context, target *querypb.Target, sql string, bindVariables map[string]*querypb.BindVariable, transactionID int64, reservedID int64, options *querypb.ExecuteOptions, callback func(*sqltypes.Result) error) error { - if !strings.Contains(sql, "moving1") { - sq.t.Errorf("Vertical Split Diff operation should only operate on the 'moving1' table. query: %v", sql) - } - - if strings.Contains(sql, "WHERE `keyspace_id`") { - sq.t.Errorf("Sql query for VerticalSplitDiff should never contain a keyspace_id WHERE clause; query received: %v", sql) - } - - sq.t.Logf("verticalDiffTabletServer: got query: %v", sql) - - // Send the headers - if err := callback(&sqltypes.Result{ - Fields: []*querypb.Field{ - { - Name: "id", - Type: sqltypes.Int64, - }, - { - Name: "msg", - Type: sqltypes.VarChar, - }, - }, - }); err != nil { - return err - } - - // Send the values - for i := 0; i < 1000; i++ { - if err := callback(&sqltypes.Result{ - Rows: [][]sqltypes.Value{ - { - sqltypes.NewInt64(int64(i)), - sqltypes.NewVarBinary(fmt.Sprintf("Text for %v", i)), - }, - }, - }); err != nil { - return err - } - } - return nil -} - -// TODO(aaijazi): Create a test in which source and destination data does not match - -func TestVerticalSplitDiff(t *testing.T) { - delay := discovery.GetTabletPickerRetryDelay() - defer func() { - discovery.SetTabletPickerRetryDelay(delay) - }() - discovery.SetTabletPickerRetryDelay(5 * time.Millisecond) - - ts := memorytopo.NewServer("cell1", "cell2") - ctx := context.Background() - wi := NewInstance(ts, "cell1", time.Second) - - sourcePrimary := testlib.NewFakeTablet(t, wi.wr, "cell1", 0, - topodatapb.TabletType_PRIMARY, nil, testlib.TabletKeyspaceShard(t, "source_ks", "0")) - sourceRdonly1 := testlib.NewFakeTablet(t, wi.wr, "cell1", 1, - topodatapb.TabletType_RDONLY, nil, testlib.TabletKeyspaceShard(t, "source_ks", "0")) - sourceRdonly2 := testlib.NewFakeTablet(t, wi.wr, "cell1", 2, - topodatapb.TabletType_RDONLY, nil, testlib.TabletKeyspaceShard(t, "source_ks", "0")) - - // Create the destination keyspace with the appropriate ServedFromMap - ki := &topodatapb.Keyspace{ - ServedFroms: []*topodatapb.Keyspace_ServedFrom{ - { - TabletType: topodatapb.TabletType_PRIMARY, - Keyspace: "source_ks", - }, - { - TabletType: topodatapb.TabletType_REPLICA, - Keyspace: "source_ks", - }, - { - TabletType: topodatapb.TabletType_RDONLY, - Keyspace: "source_ks", - }, - }, - } - wi.wr.TopoServer().CreateKeyspace(ctx, "destination_ks", ki) - - destPrimary := testlib.NewFakeTablet(t, wi.wr, "cell1", 10, - topodatapb.TabletType_PRIMARY, nil, testlib.TabletKeyspaceShard(t, "destination_ks", "0")) - destRdonly1 := testlib.NewFakeTablet(t, wi.wr, "cell1", 11, - topodatapb.TabletType_RDONLY, nil, testlib.TabletKeyspaceShard(t, "destination_ks", "0")) - destRdonly2 := testlib.NewFakeTablet(t, wi.wr, "cell1", 12, - topodatapb.TabletType_RDONLY, nil, testlib.TabletKeyspaceShard(t, "destination_ks", "0")) - - wi.wr.SetSourceShards(ctx, "destination_ks", "0", []*topodatapb.TabletAlias{sourceRdonly1.Tablet.Alias}, []string{"moving.*", "view1"}) - - // add the topo and schema data we'll need - if err := topotools.RebuildKeyspace(ctx, wi.wr.Logger(), wi.wr.TopoServer(), "source_ks", nil, false); err != nil { - t.Fatalf("RebuildKeyspaceGraph failed: %v", err) - } - if err := topotools.RebuildKeyspace(ctx, wi.wr.Logger(), wi.wr.TopoServer(), "destination_ks", nil, false); err != nil { - t.Fatalf("RebuildKeyspaceGraph failed: %v", err) - } - - for _, rdonly := range []*testlib.FakeTablet{sourceRdonly1, sourceRdonly2, destRdonly1, destRdonly2} { - // both source and destination have the table definition for 'moving1'. - // source also has "staying1" while destination has "extra1". - // (Both additional tables should be ignored by the diff.) - extraTable := "staying1" - if rdonly == destRdonly1 || rdonly == destRdonly2 { - extraTable = "extra1" - } - rdonly.FakeMysqlDaemon.Schema = &tabletmanagerdatapb.SchemaDefinition{ - DatabaseSchema: "", - TableDefinitions: []*tabletmanagerdatapb.TableDefinition{ - { - Name: "moving1", - Columns: []string{"id", "msg"}, - PrimaryKeyColumns: []string{"id"}, - Type: tmutils.TableBaseTable, - }, - { - Name: extraTable, - Columns: []string{"id", "msg"}, - PrimaryKeyColumns: []string{"id"}, - Type: tmutils.TableBaseTable, - }, - { - Name: "view1", - Type: tmutils.TableView, - }, - }, - } - qs := fakes.NewStreamHealthQueryService(rdonly.Target()) - qs.AddDefaultHealthResponse() - grpcqueryservice.Register(rdonly.RPCServer, &verticalDiffTabletServer{ - t: t, - - StreamHealthQueryService: qs, - }) - } - - // Start action loop after having registered all RPC services. - for _, ft := range []*testlib.FakeTablet{sourcePrimary, sourceRdonly1, sourceRdonly2, destPrimary, destRdonly1, destRdonly2} { - ft.StartActionLoop(t, wi.wr) - defer ft.StopActionLoop(t) - } - - // Run the vtworker command. - args := []string{"VerticalSplitDiff", "destination_ks/0"} - // We need to use FakeTabletManagerClient because we don't - // have a good way to fake the binlog player yet, which is - // necessary for synchronizing replication. - wr := wrangler.New(logutil.NewConsoleLogger(), ts, newFakeTMCTopo(ts)) - if err := runCommand(t, wi, wr, args); err != nil { - t.Fatal(err) - } -} diff --git a/go/vt/worker/vtworkerclient/interface.go b/go/vt/worker/vtworkerclient/interface.go deleted file mode 100644 index 7d4e78cbb61..00000000000 --- a/go/vt/worker/vtworkerclient/interface.go +++ /dev/null @@ -1,75 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Package vtworkerclient contains the generic client side of the remote vtworker protocol. -package vtworkerclient - -import ( - "flag" - - "context" - - "vitess.io/vitess/go/vt/log" - "vitess.io/vitess/go/vt/logutil" - "vitess.io/vitess/go/vt/proto/vtrpc" - "vitess.io/vitess/go/vt/vterrors" -) - -// protocol specifics which RPC client implementation should be used. -var protocol = flag.String("vtworker_client_protocol", "grpc", "(DEPRECATED) the protocol to use to talk to the vtworker server") - -// Client defines the interface used to send remote vtworker commands -type Client interface { - // ExecuteVtworkerCommand will execute the command remotely. - ExecuteVtworkerCommand(ctx context.Context, args []string) (logutil.EventStream, error) - - // Close will terminate the connection. This object won't be - // used after this. - Close() -} - -// Factory functions are registered by client implementations. -type Factory func(addr string) (Client, error) - -var factories = make(map[string]Factory) - -// RegisterFactory allows a client implementation to register itself. -func RegisterFactory(name string, factory Factory) { - if _, ok := factories[name]; ok { - log.Fatalf("RegisterFactory: %s already exists", name) - } - factories[name] = factory -} - -// UnregisterFactoryForTest allows to unregister a client implementation from the static map. -// This function is used by unit tests to cleanly unregister any fake implementations. -// This way, a test package can use the same name for different fakes and no dangling fakes are -// left behind in the static factories map after the test. -func UnregisterFactoryForTest(name string) { - if _, ok := factories[name]; !ok { - log.Fatalf("UnregisterFactoryForTest: %s is not registered", name) - } - delete(factories, name) -} - -// New allows a user of the client library to get its implementation. -func New(addr string) (Client, error) { - factory, ok := factories[*protocol] - if !ok { - return nil, vterrors.Errorf(vtrpc.Code_INVALID_ARGUMENT, "unknown vtworker client protocol: %v", *protocol) - } - return factory(addr) -} diff --git a/go/vt/worker/vtworkerclient/wrapper.go b/go/vt/worker/vtworkerclient/wrapper.go deleted file mode 100644 index c129fa954b9..00000000000 --- a/go/vt/worker/vtworkerclient/wrapper.go +++ /dev/null @@ -1,60 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package vtworkerclient - -import ( - "io" - - "context" - - "vitess.io/vitess/go/vt/vterrors" - - logutilpb "vitess.io/vitess/go/vt/proto/logutil" -) - -// RunCommandAndWait executes a single command on a given vtworker and blocks until the command did return or timed out. -// Output from vtworker is streamed as logutil.Event messages which -// have to be consumed by the caller who has to specify a "recv" function. -func RunCommandAndWait(ctx context.Context, server string, args []string, recv func(*logutilpb.Event)) error { - if recv == nil { - panic("no function closure for Event stream specified") - } - // create the client - client, err := New(server) - if err != nil { - return vterrors.Wrapf(err, "cannot dial to server %v", server) - } - defer client.Close() - - // run the command - stream, err := client.ExecuteVtworkerCommand(ctx, args) - if err != nil { - return vterrors.Wrap(err, "cannot execute remote command") - } - - for { - e, err := stream.Recv() - switch err { - case nil: - recv(e) - case io.EOF: - return nil - default: - return vterrors.Wrap(err, "stream error") - } - } -} diff --git a/go/vt/worker/vtworkerclienttest/client_testsuite.go b/go/vt/worker/vtworkerclienttest/client_testsuite.go deleted file mode 100644 index bbaa027006f..00000000000 --- a/go/vt/worker/vtworkerclienttest/client_testsuite.go +++ /dev/null @@ -1,289 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Package vtworkerclienttest contains the testsuite against which each -// RPC implementation of the vtworkerclient interface must be tested. -package vtworkerclienttest - -// NOTE: This file is not test-only code because it is referenced by -// tests in other packages and therefore it has to be regularly -// visible. - -// NOTE: This code is in its own package such that its dependencies -// (e.g. zookeeper) won't be drawn into production binaries as well. - -import ( - "io" - "strings" - "sync" - "testing" - "time" - - "context" - - "vitess.io/vitess/go/vt/logutil" - "vitess.io/vitess/go/vt/topo/memorytopo" - "vitess.io/vitess/go/vt/vterrors" - "vitess.io/vitess/go/vt/vttablet/tmclient" - "vitess.io/vitess/go/vt/worker" - "vitess.io/vitess/go/vt/worker/vtworkerclient" - - // Import the gRPC client implementation for tablet manager because the real - // vtworker implementation requires it. - _ "vitess.io/vitess/go/vt/vttablet/grpctmclient" - - vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" -) - -func init() { - // enforce we will use the right protocol (gRPC) (note the - // client is unused, but it is initialized, so it needs to exist) - *tmclient.TabletManagerProtocol = "grpc" -} - -// CreateWorkerInstance returns a properly configured vtworker instance. -func CreateWorkerInstance(t *testing.T) *worker.Instance { - ts := memorytopo.NewServer("cell1", "cell2") - return worker.NewInstance(ts, "cell1", 1*time.Second) -} - -// TestSuite runs the test suite on the given vtworker and vtworkerclient. -func TestSuite(t *testing.T, c vtworkerclient.Client) { - commandSucceeds(t, c) - - commandErrors(t, c) - - commandErrorsBecauseBusy(t, c, false /* client side cancelation */) - - commandErrorsBecauseBusy(t, c, true /* server side cancelation */) - - commandPanics(t, c) -} - -func commandSucceeds(t *testing.T, client vtworkerclient.Client) { - stream, err := client.ExecuteVtworkerCommand(context.Background(), []string{"Ping", "pong"}) - if err != nil { - t.Fatalf("Cannot execute remote command: %v", err) - } - - got, err := stream.Recv() - if err != nil { - t.Fatalf("failed to get first line: %v", err) - } - expected := "Ping command was called with message: 'pong'.\n" - if logutil.EventString(got) != expected { - t.Errorf("Got unexpected log line '%v' expected '%v'", got.String(), expected) - } - _, err = stream.Recv() - if err != io.EOF { - t.Fatalf("Didn't get EOF as expected: %v", err) - } - - // Reset vtworker for the next test function. - if err := runVtworkerCommand(client, []string{"Reset"}); err != nil { - t.Fatal(err) - } -} - -func runVtworkerCommand(client vtworkerclient.Client, args []string) error { - stream, err := client.ExecuteVtworkerCommand(context.Background(), args) - if err != nil { - return vterrors.Wrap(err, "cannot execute remote command") - } - - for { - _, err := stream.Recv() - switch err { - case nil: - // Consume next response. - case io.EOF: - return nil - default: - return vterrors.Wrap(err, "unexpected error when reading the stream") - } - } -} - -// commandErrorsBecauseBusy tests that concurrent commands are rejected with -// TRANSIENT_ERROR while a command is already running. -// It also tests the correct propagation of the CANCELED error code. -func commandErrorsBecauseBusy(t *testing.T, client vtworkerclient.Client, serverSideCancelation bool) { - // Run the vtworker "Block" command which blocks until we cancel the context. - var wg sync.WaitGroup - ctx, cancel := context.WithCancel(context.Background()) - // blockCommandStarted will be closed after we're sure that vtworker is - // running the "Block" command. - blockCommandStarted := make(chan struct{}) - var errorCodeCheck error - wg.Add(1) - errChan := make(chan error, 1) - defer close(errChan) - go func() { - defer wg.Done() - stream, err := client.ExecuteVtworkerCommand(ctx, []string{"Block"}) - if err != nil { - errChan <- err - close(blockCommandStarted) - return - } else { - errChan <- nil - } - - firstLineReceived := false - for { - if _, err := stream.Recv(); err != nil { - // We see CANCELED from the RPC client (client side cancelation) or - // from vtworker itself (server side cancelation). - if vterrors.Code(err) != vtrpcpb.Code_CANCELED { - errorCodeCheck = vterrors.Wrap(err, "Block command should only error due to canceled context") - } - // Stream has finished. - break - } - - if !firstLineReceived { - firstLineReceived = true - // The first log line will come from the "Block" command, so we are sure - // now that vtworker is actually executing it. - close(blockCommandStarted) - } - } - }() - - // Try to run a second, concurrent vtworker command. - // vtworker should send an error back that it's busy and we should retry later. - <-blockCommandStarted - gotErr := runVtworkerCommand(client, []string{"Ping", "Are you busy?"}) - wantCode := vtrpcpb.Code_UNAVAILABLE - if gotCode := vterrors.Code(gotErr); gotCode != wantCode { - t.Fatalf("wrong error code for second cmd: got = %v, want = %v, err: %v", gotCode, wantCode, gotErr) - } - - // Cancel running "Block" command. - if serverSideCancelation { - if err := runVtworkerCommand(client, []string{"Cancel"}); err != nil { - t.Fatal(err) - } - } - // Always cancel the context to not leak it (regardless of client or server - // side cancelation). - cancel() - - wg.Wait() - if err := <-errChan; err != nil { - t.Fatalf("Block command should not have failed: %v", err) - } - if errorCodeCheck != nil { - t.Fatalf("Block command did not return the CANCELED error code: %v", errorCodeCheck) - } - - // vtworker is now in a special state where the current job is already - // canceled but not reset yet. New commands are still failing with a - // retryable error. - gotErr2 := runVtworkerCommand(client, []string{"Ping", "canceled and still busy?"}) - wantCode2 := vtrpcpb.Code_UNAVAILABLE - if gotCode2 := vterrors.Code(gotErr2); gotCode2 != wantCode2 { - t.Fatalf("wrong error code for second cmd before reset: got = %v, want = %v, err: %v", gotCode2, wantCode2, gotErr2) - } - - // Reset vtworker for the next test function. - if err := resetVtworker(t, client); err != nil { - t.Fatal(err) - } - - // Second vtworker command should succeed now after the first has finished. - if err := runVtworkerCommand(client, []string{"Ping", "You should not be busy anymore!"}); err != nil { - t.Fatalf("second cmd should not have failed: %v", err) - } - - // Reset vtworker for the next test function. - if err := runVtworkerCommand(client, []string{"Reset"}); err != nil { - t.Fatal(err) - } -} - -// resetVtworker will retry to "Reset" vtworker until it succeeds. -// Retries are necessary to cope with the following race: -// a) RPC started vtworker command e.g. "Block". -// b) client cancels RPC and triggers vtworker to cancel the running command. -// c) RPC returns with a response after cancelation was received by vtworker. -// d) vtworker is still canceling and shutting down the command. -// e) A new vtworker command e.g. "Reset" would fail at this point with -// "vtworker still executing" until the cancelation is complete. -func resetVtworker(t *testing.T, client vtworkerclient.Client) error { - start := time.Now() - attempts := 0 - for { - attempts++ - err := runVtworkerCommand(client, []string{"Reset"}) - - if err == nil { - return nil - } - - if time.Since(start) > 5*time.Second { - return vterrors.Wrapf(err, "Reset was not successful after 5s and %d attempts", attempts) - } - - if !strings.Contains(err.Error(), "worker still executing") { - return vterrors.Wrap(err, "Reset must not fail") - } - - t.Logf("retrying to Reset vtworker because the previous command has not finished yet. got err: %v", err) - continue - } -} - -func commandErrors(t *testing.T, client vtworkerclient.Client) { - stream, err := client.ExecuteVtworkerCommand(context.Background(), []string{"NonexistingCommand"}) - // The expected error could already be seen now or after the output channel is closed. - // To avoid checking for the same error twice, we don't check it here yet. - - if err == nil { - // Don't check for errors until the output channel is closed. - // We expect the usage to be sent as output. However, we have to consider it - // optional and do not test for it because not all RPC implementations send - // the output after an error. - for { - _, err = stream.Recv() - if err != nil { - break - } - } - } - - expected := "unknown command: NonexistingCommand" - if err == nil || !strings.Contains(err.Error(), expected) { - t.Fatalf("Unexpected remote error, got: '%v' was expecting to find '%v'", err, expected) - } -} - -func commandPanics(t *testing.T, client vtworkerclient.Client) { - stream, err := client.ExecuteVtworkerCommand(context.Background(), []string{"Panic"}) - // The expected error could already be seen now or after the output channel is closed. - // To avoid checking for the same error twice, we don't check it here yet. - - if err == nil { - // Don't check for errors until the output channel is closed. - // No output expected in this case. - _, err = stream.Recv() - } - - expected := "uncaught vtworker panic: Panic command was called. This should be caught by the vtworker framework and logged as an error." - if err == nil || !strings.Contains(err.Error(), expected) { - t.Fatalf("Unexpected remote error, got: '%v' was expecting to find '%v'", err, expected) - } -} diff --git a/go/vt/worker/worker.go b/go/vt/worker/worker.go deleted file mode 100644 index b34d94e8963..00000000000 --- a/go/vt/worker/worker.go +++ /dev/null @@ -1,159 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -/* -Package worker contains the framework, utility methods and core -functions for long running actions. 'vtworker' binary will use these. -*/ -package worker - -import ( - "flag" - "html/template" - "time" - - "context" - - "vitess.io/vitess/go/stats" -) - -// Worker is the base interface for all long running workers. -type Worker interface { - // State returns the current state using the internal representation. - State() StatusWorkerState - - // StatusAsHTML returns the current worker status in HTML. - StatusAsHTML() template.HTML - - // StatusAsText returns the current worker status in plain text. - StatusAsText() string - - // Run is the main entry point for the worker. It will be - // called in a go routine. When the passed in context is canceled, Run() - // should exit as soon as possible. - Run(context.Context) error -} - -var ( - retryDuration = flag.Duration("retry_duration", 2*time.Hour, "Amount of time we wait before giving up on a retryable action (e.g. write to destination, waiting for healthy tablets)") - executeFetchRetryTime = flag.Duration("executefetch_retry_time", 30*time.Second, "Amount of time we should wait before retrying ExecuteFetch calls") - remoteActionsTimeout = flag.Duration("remote_actions_timeout", time.Minute, "Amount of time to wait for remote actions (like replication stop, ...)") - _ = flag.Bool("use_v3_resharding_mode", true, "True iff the workers should use V3-style resharding, which doesn't require a preset sharding key column.") - - healthCheckTopologyRefresh = flag.Duration("worker_healthcheck_topology_refresh", 30*time.Second, "refresh interval for re-reading the topology") - healthcheckRetryDelay = flag.Duration("worker_healthcheck_retry_delay", 5*time.Second, "delay before retrying a failed healthcheck") - healthCheckTimeout = flag.Duration("worker_healthcheck_timeout", time.Minute, "the health check timeout period") - - statsState = stats.NewString("WorkerState") - statsRetryCount = stats.NewCounter("WorkerRetryCount", "Total number of times a query to a vttablet had to be retried") - statsRetryCounters = stats.NewCountersWithSingleLabel("WorkerRetryCounters", "Number of retries grouped by category e.g. TimeoutError or ReadOnly", "category") - statsThrottledCounters = stats.NewCountersWithMultiLabels( - "WorkerThrottledCounters", - `Number of times a write has been throttled grouped by (keyspace, shard, threadID). - Mainly used for testing. If throttling is enabled this should always be non-zero for all threads`, - []string{"Keyspace", "ShardName", "ThreadId"}) - statsStateDurationsNs = stats.NewGaugesWithSingleLabel("WorkerStateDurations", "How much time was spent in each state. Mainly used for testing.", "state") - - statsOnlineInsertsCounters = stats.NewCountersWithSingleLabel( - "WorkerOnlineInsertsCounters", - "For every table how many rows were inserted during the online clone (reconciliation) phase", - "table") - statsOnlineUpdatesCounters = stats.NewCountersWithSingleLabel( - "WorkerOnlineUpdatesCounters", - "For every table how many rows were updated", - "table") - statsOnlineDeletesCounters = stats.NewCountersWithSingleLabel( - "WorkerOnlineDeletesCounters", - "For every table how many rows were deleted", - "table") - statsOnlineEqualRowsCounters = stats.NewCountersWithSingleLabel( - "WorkerOnlineEqualRowsCounters", - "For every table how many rows were equal", - "table") - - statsOfflineInsertsCounters = stats.NewCountersWithSingleLabel( - "WorkerOfflineInsertsCounters", - "For every table how many rows were inserted during the online clone (reconciliation) phase", - "table") - statsOfflineUpdatesCounters = stats.NewCountersWithSingleLabel( - "WorkerOfflineUpdatesCounters", - "For every table how many rows were updated", - "table") - statsOfflineDeletesCounters = stats.NewCountersWithSingleLabel( - "WorkerOfflineDeletesCounters", - "For every table how many rows were deleted", - "table") - statsOfflineEqualRowsCounters = stats.NewCountersWithSingleLabel( - "WorkerOfflineEqualRowsCounters", - "For every table how many rows were equal", - "table") - - statsStreamingQueryCounters = stats.NewCountersWithSingleLabel( - "StreamingQueryCounters", - "For every tablet alias how often a streaming query was successfully established there", - "tablet_alias") - statsStreamingQueryErrorsCounters = stats.NewCountersWithSingleLabel( - "StreamingQueryErrorsCounters", - "For every tablet alias how often a (previously successfully established) streaming query did error", - "tablet_alias") - statsStreamingQueryRestartsSameTabletCounters = stats.NewCountersWithSingleLabel( - "StreamingQueryRestartsSameTabletCounters", - `For every tablet alias how often we successfully restarted a streaming query on the first retry. - This kind of restart is usually necessary when our streaming query is idle and MySQL aborts it after a timeout.`, - "tablet_alias") - statsStreamingQueryRestartsDifferentTablet = stats.NewCounter( - "StreamingQueryRestartsDifferentTablet", - `How many restarts were successful on the 2 (or higher) retry after the initial retry to the same tablet fails we switch to a different tablet. - In practice, this happens when a tablet did go away due to a maintenance operation.`) -) - -const ( - retryCategoryReadOnly = "ReadOnly" - retryCategoryTimeoutError = "TimeoutError" - retryCategoryConnectionError = "ConnectionError" - retryCategoryNoPrimaryAvailable = "NoPrimaryAvailable" -) - -// resetVars resets the debug variables that are meant to provide information on a -// per-run basis. This should be called at the beginning of each worker run. -func resetVars() { - statsState.Set("") - statsRetryCount.Reset() - statsRetryCounters.ResetAll() - - statsOnlineInsertsCounters.ResetAll() - statsOnlineUpdatesCounters.ResetAll() - statsOnlineDeletesCounters.ResetAll() - statsOnlineEqualRowsCounters.ResetAll() - - statsOfflineInsertsCounters.ResetAll() - statsOfflineUpdatesCounters.ResetAll() - statsOfflineDeletesCounters.ResetAll() - statsOfflineEqualRowsCounters.ResetAll() - - statsStreamingQueryCounters.ResetAll() - statsStreamingQueryErrorsCounters.ResetAll() -} - -// checkDone returns ctx.Err() iff ctx.Done(). -func checkDone(ctx context.Context) error { - select { - case <-ctx.Done(): - return ctx.Err() - default: - } - return nil -} diff --git a/go/vt/workflow/resharding/mock_resharding_wrangler_test.go b/go/vt/workflow/resharding/mock_resharding_wrangler_test.go deleted file mode 100644 index 981ef209481..00000000000 --- a/go/vt/workflow/resharding/mock_resharding_wrangler_test.go +++ /dev/null @@ -1,80 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: resharding_wrangler.go - -// Package resharding is a generated GoMock package. -package resharding - -import ( - context "context" - reflect "reflect" - time "time" - - gomock "github.com/golang/mock/gomock" - - topodata "vitess.io/vitess/go/vt/proto/topodata" -) - -// MockReshardingWrangler is a mock of Wrangler interface. -type MockReshardingWrangler struct { - ctrl *gomock.Controller - recorder *MockReshardingWranglerMockRecorder -} - -// MockReshardingWranglerMockRecorder is the mock recorder for MockReshardingWrangler. -type MockReshardingWranglerMockRecorder struct { - mock *MockReshardingWrangler -} - -// NewMockReshardingWrangler creates a new mock instance. -func NewMockReshardingWrangler(ctrl *gomock.Controller) *MockReshardingWrangler { - mock := &MockReshardingWrangler{ctrl: ctrl} - mock.recorder = &MockReshardingWranglerMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockReshardingWrangler) EXPECT() *MockReshardingWranglerMockRecorder { - return m.recorder -} - -// CopySchemaShardFromShard mocks base method. -func (m *MockReshardingWrangler) CopySchemaShardFromShard(ctx context.Context, tables, excludeTables []string, includeViews bool, sourceKeyspace, sourceShard, destKeyspace, destShard string, waitReplicasTimeout time.Duration, skipVerify bool) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CopySchemaShardFromShard", ctx, tables, excludeTables, includeViews, sourceKeyspace, sourceShard, destKeyspace, destShard, waitReplicasTimeout, skipVerify) - ret0, _ := ret[0].(error) - return ret0 -} - -// CopySchemaShardFromShard indicates an expected call of CopySchemaShardFromShard. -func (mr *MockReshardingWranglerMockRecorder) CopySchemaShardFromShard(ctx, tables, excludeTables, includeViews, sourceKeyspace, sourceShard, destKeyspace, destShard, waitReplicasTimeout, skipVerify any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CopySchemaShardFromShard", reflect.TypeOf((*MockReshardingWrangler)(nil).CopySchemaShardFromShard), ctx, tables, excludeTables, includeViews, sourceKeyspace, sourceShard, destKeyspace, destShard, waitReplicasTimeout, skipVerify) -} - -// MigrateServedTypes mocks base method. -func (m *MockReshardingWrangler) MigrateServedTypes(ctx context.Context, keyspace, shard string, cells []string, servedType topodata.TabletType, reverse, skipReFreshState bool, filteredReplicationWaitTime time.Duration, reverseReplication bool) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "MigrateServedTypes", ctx, keyspace, shard, cells, servedType, reverse, skipReFreshState, filteredReplicationWaitTime, reverseReplication) - ret0, _ := ret[0].(error) - return ret0 -} - -// MigrateServedTypes indicates an expected call of MigrateServedTypes. -func (mr *MockReshardingWranglerMockRecorder) MigrateServedTypes(ctx, keyspace, shard, cells, servedType, reverse, skipReFreshState, filteredReplicationWaitTime, reverseReplication any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MigrateServedTypes", reflect.TypeOf((*MockReshardingWrangler)(nil).MigrateServedTypes), ctx, keyspace, shard, cells, servedType, reverse, skipReFreshState, filteredReplicationWaitTime, reverseReplication) -} - -// WaitForFilteredReplication mocks base method. -func (m *MockReshardingWrangler) WaitForFilteredReplication(ctx context.Context, keyspace, shard string, maxDelay time.Duration) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "WaitForFilteredReplication", ctx, keyspace, shard, maxDelay) - ret0, _ := ret[0].(error) - return ret0 -} - -// WaitForFilteredReplication indicates an expected call of WaitForFilteredReplication. -func (mr *MockReshardingWranglerMockRecorder) WaitForFilteredReplication(ctx, keyspace, shard, maxDelay any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WaitForFilteredReplication", reflect.TypeOf((*MockReshardingWrangler)(nil).WaitForFilteredReplication), ctx, keyspace, shard, maxDelay) -} diff --git a/go/vt/workflow/resharding/resharding_wrangler.go b/go/vt/workflow/resharding/resharding_wrangler.go deleted file mode 100644 index 2ae7f7b5f9a..00000000000 --- a/go/vt/workflow/resharding/resharding_wrangler.go +++ /dev/null @@ -1,37 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Command to generate a mock for this interface with mockgen. -//go:generate mockgen -source resharding_wrangler.go -destination mock_resharding_wrangler_test.go -package resharding -mock_names "Wrangler=MockReshardingWrangler" - -package resharding - -import ( - "time" - - "context" - - topodatapb "vitess.io/vitess/go/vt/proto/topodata" -) - -// Wrangler is the interface to be used in creating mock interface for wrangler, which is used for unit test. It includes a subset of the methods in go/vt/Wrangler. -type Wrangler interface { - CopySchemaShardFromShard(ctx context.Context, tables, excludeTables []string, includeViews bool, sourceKeyspace, sourceShard, destKeyspace, destShard string, waitReplicasTimeout time.Duration, skipVerify bool) error - - WaitForFilteredReplication(ctx context.Context, keyspace, shard string, maxDelay time.Duration) error - - MigrateServedTypes(ctx context.Context, keyspace, shard string, cells []string, servedType topodatapb.TabletType, reverse, skipReFreshState bool, filteredReplicationWaitTime time.Duration, reverseReplication bool) error -} diff --git a/go/vt/workflow/resharding/tasks.go b/go/vt/workflow/resharding/tasks.go deleted file mode 100644 index 2c343948952..00000000000 --- a/go/vt/workflow/resharding/tasks.go +++ /dev/null @@ -1,164 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package resharding - -import ( - "fmt" - "log" - "strings" - - "context" - - "vitess.io/vitess/go/vt/automation" - "vitess.io/vitess/go/vt/topo/topoproto" - "vitess.io/vitess/go/vt/workflow" - "vitess.io/vitess/go/vt/wrangler" - - topodatapb "vitess.io/vitess/go/vt/proto/topodata" - workflowpb "vitess.io/vitess/go/vt/proto/workflow" -) - -func createTaskID(phase workflow.PhaseType, shardName string) string { - return fmt.Sprintf("%s/%s", phase, shardName) -} - -// GetTasks returns selected tasks for a phase from the checkpoint -// with expected execution order. -func (hw *horizontalReshardingWorkflow) GetTasks(phase workflow.PhaseType) []*workflowpb.Task { - var shards []string - switch phase { - case phaseCopySchema, phaseWaitForFilteredReplication: - shards = strings.Split(hw.checkpoint.Settings["destination_shards"], ",") - case phaseClone, phaseMigrateRdonly, phaseMigrateReplica, phaseMigrateMaster: - shards = strings.Split(hw.checkpoint.Settings["source_shards"], ",") - case phaseDiff: - switch hw.checkpoint.Settings["split_diff_cmd"] { - case "SplitDiff": - shards = strings.Split(hw.checkpoint.Settings["destination_shards"], ",") - case "MultiSplitDiff": - shards = strings.Split(hw.checkpoint.Settings["source_shards"], ",") - } - default: - log.Fatalf("BUG: unknown phase type: %v", phase) - } - - var tasks []*workflowpb.Task - for _, s := range shards { - taskID := createTaskID(phase, s) - tasks = append(tasks, hw.checkpoint.Tasks[taskID]) - } - return tasks -} - -func (hw *horizontalReshardingWorkflow) runCopySchema(ctx context.Context, t *workflowpb.Task) error { - keyspace := t.Attributes["keyspace"] - sourceShard := t.Attributes["source_shard"] - destShard := t.Attributes["destination_shard"] - excludeTables := strings.Split(t.Attributes["exclude_tables"], ",") - return hw.wr.CopySchemaShardFromShard(ctx, nil /* tableArray*/, excludeTables /* excludeTableArray */, true, /*includeViews*/ - keyspace, sourceShard, keyspace, destShard, wrangler.DefaultWaitReplicasTimeout, false) -} - -func (hw *horizontalReshardingWorkflow) runSplitClone(ctx context.Context, t *workflowpb.Task) error { - keyspace := t.Attributes["keyspace"] - sourceShard := t.Attributes["source_shard"] - worker := t.Attributes["vtworker"] - minHealthyRdonlyTablets := t.Attributes["min_healthy_rdonly_tablets"] - splitCmd := t.Attributes["split_cmd"] - useConsistentSnapshot := t.Attributes["use_consistent_snapshot"] - - sourceKeyspaceShard := topoproto.KeyspaceShardString(keyspace, sourceShard) - excludeTables := t.Attributes["exclude_tables"] - // Reset the vtworker to avoid error if vtworker command has been called elsewhere. - // This is because vtworker class doesn't cleanup the environment after execution. - if _, err := automation.ExecuteVtworker(ctx, worker, []string{"Reset"}); err != nil { - return err - } - - args := []string{splitCmd, "--min_healthy_rdonly_tablets=" + minHealthyRdonlyTablets} - if useConsistentSnapshot != "" { - args = append(args, "--use_consistent_snapshot") - } - - if excludeTables != "" { - args = append(args, fmt.Sprintf("--exclude_tables=%s", excludeTables)) - } - - args = append(args, sourceKeyspaceShard) - - _, err := automation.ExecuteVtworker(hw.ctx, worker, args) - return err -} - -func (hw *horizontalReshardingWorkflow) runWaitForFilteredReplication(ctx context.Context, t *workflowpb.Task) error { - keyspace := t.Attributes["keyspace"] - destShard := t.Attributes["destination_shard"] - return hw.wr.WaitForFilteredReplication(ctx, keyspace, destShard, wrangler.DefaultWaitForFilteredReplicationMaxDelay) -} - -func (hw *horizontalReshardingWorkflow) runSplitDiff(ctx context.Context, t *workflowpb.Task) error { - keyspace := t.Attributes["keyspace"] - splitDiffCmd := t.Attributes["split_diff_cmd"] - destShard := t.Attributes["destination_shard"] - sourceShard := t.Attributes["source_shard"] - destinationTabletType := t.Attributes["dest_tablet_type"] - worker := t.Attributes["vtworker"] - useConsistentSnapshot := t.Attributes["use_consistent_snapshot"] - excludeTables := t.Attributes["exclude_tables"] - - if _, err := automation.ExecuteVtworker(hw.ctx, worker, []string{"Reset"}); err != nil { - return err - } - args := []string{splitDiffCmd} - - if useConsistentSnapshot != "" { - args = append(args, "--use_consistent_snapshot") - } - - if excludeTables != "" { - args = append(args, fmt.Sprintf("--exclude_tables=%s", excludeTables)) - } - - switch splitDiffCmd { - case "SplitDiff": - args = append(args, "--min_healthy_rdonly_tablets=1", "--dest_tablet_type="+destinationTabletType, topoproto.KeyspaceShardString(keyspace, destShard)) - case "MultiSplitDiff": - args = append(args, "--min_healthy_tablets=1", "--tablet_type="+destinationTabletType, topoproto.KeyspaceShardString(keyspace, sourceShard)) - } - - _, err := automation.ExecuteVtworker(ctx, worker, args) - return err -} - -func (hw *horizontalReshardingWorkflow) runMigrate(ctx context.Context, t *workflowpb.Task) error { - keyspace := t.Attributes["keyspace"] - sourceShard := t.Attributes["source_shard"] - servedTypeStr := t.Attributes["served_type"] - - servedType, err := topoproto.ParseTabletType(servedTypeStr) - if err != nil { - return fmt.Errorf("unknown tablet type: %v", servedTypeStr) - } - - if servedType != topodatapb.TabletType_RDONLY && - servedType != topodatapb.TabletType_REPLICA && - servedType != topodatapb.TabletType_PRIMARY { - return fmt.Errorf("wrong served type to be migrated: %v", servedTypeStr) - } - - return hw.wr.MigrateServedTypes(ctx, keyspace, sourceShard, nil /* cells */, servedType, false /* reverse */, false /* skipReFreshState */, wrangler.DefaultFilteredReplicationWaitTime, false /* reverseReplication */) -} diff --git a/go/vt/workflow/resharding/workflow.go b/go/vt/workflow/resharding/workflow.go deleted file mode 100644 index 1bb75c00fd5..00000000000 --- a/go/vt/workflow/resharding/workflow.go +++ /dev/null @@ -1,497 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package resharding - -// Package resharding contains a workflow for automatic horizontal resharding. -// The workflow assumes that there are as many vtworker processes running as source shards. -// Plus, these vtworker processes must be reachable via RPC. - -import ( - "flag" - "fmt" - "strconv" - "strings" - - "context" - - "google.golang.org/protobuf/proto" - - "vitess.io/vitess/go/vt/log" - "vitess.io/vitess/go/vt/logutil" - "vitess.io/vitess/go/vt/topo" - "vitess.io/vitess/go/vt/topotools" - "vitess.io/vitess/go/vt/vttablet/tmclient" - "vitess.io/vitess/go/vt/workflow" - "vitess.io/vitess/go/vt/wrangler" - - topodatapb "vitess.io/vitess/go/vt/proto/topodata" - workflowpb "vitess.io/vitess/go/vt/proto/workflow" -) - -const ( - codeVersion = 2 - horizontalReshardingFactoryName = "horizontal_resharding" - phaseCopySchema workflow.PhaseType = "copy_schema" - phaseClone workflow.PhaseType = "clone" - phaseWaitForFilteredReplication workflow.PhaseType = "wait_for_filtered_replication" - phaseDiff workflow.PhaseType = "diff" - phaseMigrateRdonly workflow.PhaseType = "migrate_rdonly" - phaseMigrateReplica workflow.PhaseType = "migrate_replica" - phaseMigrateMaster workflow.PhaseType = "migrate_master" -) - -// Register registers the HorizontalReshardingWorkflowFactory as a factory -// in the workflow framework. -func Register() { - workflow.Register(horizontalReshardingFactoryName, &Factory{}) -} - -// Factory is the factory to create -// a horizontal resharding workflow. -type Factory struct{} - -// Init is part of the workflow.Factory interface. -func (*Factory) Init(m *workflow.Manager, w *workflowpb.Workflow, args []string) error { - subFlags := flag.NewFlagSet(horizontalReshardingFactoryName, flag.ContinueOnError) - keyspace := subFlags.String("keyspace", "", "Name of keyspace to perform horizontal resharding") - vtworkersStr := subFlags.String("vtworkers", "", "A comma-separated list of vtworker addresses") - excludeTablesStr := subFlags.String("exclude_tables", "", "A comma-separated list of tables to exclude") - sourceShardsStr := subFlags.String("source_shards", "", "A comma-separated list of source shards") - destinationShardsStr := subFlags.String("destination_shards", "", "A comma-separated list of destination shards") - minHealthyRdonlyTablets := subFlags.String("min_healthy_rdonly_tablets", "1", "Minimum number of healthy RDONLY tablets required in source shards") - skipSplitRatioCheck := subFlags.Bool("skip_split_ratio_check", false, "Skip validation on minimum number of healthy RDONLY tablets") - splitCmd := subFlags.String("split_cmd", "SplitClone", "Split command to use to perform horizontal resharding") - splitDiffCmd := subFlags.String("split_diff_cmd", "SplitDiff", "Split diff command to use to perform horizontal resharding (either SplitDiff or MultiSplitDiff)") - splitDiffDestTabletType := subFlags.String("split_diff_dest_tablet_type", "RDONLY", "Specifies tablet type to use in destination shards while performing SplitDiff operation") - phaseEnaableApprovalsDesc := fmt.Sprintf("Comma separated phases that require explicit approval in the UI to execute. Phase names are: %v", strings.Join(WorkflowPhases(), ",")) - phaseEnableApprovalsStr := subFlags.String("phase_enable_approvals", strings.Join(WorkflowPhases(), ","), phaseEnaableApprovalsDesc) - useConsistentSnapshot := subFlags.Bool("use_consistent_snapshot", false, "Instead of pausing replication on the source, uses transactions with consistent snapshot to have a stable view of the data.") - - if err := subFlags.Parse(args); err != nil { - return err - } - if *keyspace == "" || *vtworkersStr == "" || *minHealthyRdonlyTablets == "" || *splitCmd == "" || *splitDiffCmd == "" { - return fmt.Errorf("keyspace name, min healthy rdonly tablets, split command, and vtworkers information must be provided for horizontal resharding") - } - - vtworkers := strings.Split(*vtworkersStr, ",") - excludeTables := strings.Split(*excludeTablesStr, ",") - sourceShards := strings.Split(*sourceShardsStr, ",") - destinationShards := strings.Split(*destinationShardsStr, ",") - phaseEnableApprovals := parsePhaseEnableApprovals(*phaseEnableApprovalsStr) - for _, phase := range phaseEnableApprovals { - validPhase := false - for _, registeredPhase := range WorkflowPhases() { - if phase == registeredPhase { - validPhase = true - } - } - if !validPhase { - return fmt.Errorf("invalid phase in phase_enable_approvals: %v", phase) - } - } - useConsistentSnapshotArg := "" - if *useConsistentSnapshot { - useConsistentSnapshotArg = "true" - } - - err := validateWorkflow(m, *keyspace, vtworkers, sourceShards, destinationShards, *minHealthyRdonlyTablets, *skipSplitRatioCheck) - if err != nil { - return err - } - - w.Name = fmt.Sprintf("Reshard shards %v into shards %v of keyspace %v.", *keyspace, *sourceShardsStr, *destinationShardsStr) - checkpoint, err := initCheckpoint(*keyspace, vtworkers, excludeTables, sourceShards, destinationShards, *minHealthyRdonlyTablets, *splitCmd, *splitDiffCmd, *splitDiffDestTabletType, useConsistentSnapshotArg) - if err != nil { - return err - } - - checkpoint.Settings["phase_enable_approvals"] = *phaseEnableApprovalsStr - - w.Data, err = proto.Marshal(checkpoint) - if err != nil { - return err - } - return nil -} - -// Instantiate is part the workflow.Factory interface. -func (*Factory) Instantiate(m *workflow.Manager, w *workflowpb.Workflow, rootNode *workflow.Node) (workflow.Workflow, error) { - rootNode.Message = "This is a workflow to execute horizontal resharding automatically." - - checkpoint := &workflowpb.WorkflowCheckpoint{} - if err := proto.Unmarshal(w.Data, checkpoint); err != nil { - return nil, err - } - - phaseEnableApprovals := make(map[string]bool) - for _, phase := range parsePhaseEnableApprovals(checkpoint.Settings["phase_enable_approvals"]) { - phaseEnableApprovals[phase] = true - } - - hw := &horizontalReshardingWorkflow{ - checkpoint: checkpoint, - rootUINode: rootNode, - logger: logutil.NewMemoryLogger(), - wr: wrangler.New(logutil.NewConsoleLogger(), m.TopoServer(), tmclient.NewTabletManagerClient()), - topoServer: m.TopoServer(), - manager: m, - phaseEnableApprovals: phaseEnableApprovals, - } - copySchemaUINode := &workflow.Node{ - Name: "CopySchemaShard", - PathName: string(phaseCopySchema), - } - cloneUINode := &workflow.Node{ - Name: "SplitClone", - PathName: string(phaseClone), - } - waitForFilteredReplicationUINode := &workflow.Node{ - Name: "WaitForFilteredReplication", - PathName: string(phaseWaitForFilteredReplication), - } - - diffUINode := &workflow.Node{ - Name: checkpoint.Settings["split_diff_cmd"], - PathName: string(phaseDiff), - } - - migrateRdonlyUINode := &workflow.Node{ - Name: "MigrateServedTypeRDONLY", - PathName: string(phaseMigrateRdonly), - } - migrateReplicaUINode := &workflow.Node{ - Name: "MigrateServedTypeREPLICA", - PathName: string(phaseMigrateReplica), - } - migrateMasterUINode := &workflow.Node{ - Name: "MigrateServedTypeMASTER", - PathName: string(phaseMigrateMaster), - } - - hw.rootUINode.Children = []*workflow.Node{ - copySchemaUINode, - cloneUINode, - waitForFilteredReplicationUINode, - diffUINode, - migrateRdonlyUINode, - migrateReplicaUINode, - migrateMasterUINode, - } - - sourceShards := strings.Split(hw.checkpoint.Settings["source_shards"], ",") - destinationShards := strings.Split(hw.checkpoint.Settings["destination_shards"], ",") - - if err := createUINodes(hw.rootUINode, phaseCopySchema, destinationShards); err != nil { - return hw, err - } - if err := createUINodes(hw.rootUINode, phaseClone, sourceShards); err != nil { - return hw, err - } - if err := createUINodes(hw.rootUINode, phaseWaitForFilteredReplication, destinationShards); err != nil { - return hw, err - } - var shardsToUseForDiff []string - - switch hw.checkpoint.Settings["split_diff_cmd"] { - case "SplitDiff": - shardsToUseForDiff = destinationShards - case "MultiSplitDiff": - shardsToUseForDiff = sourceShards - } - - if err := createUINodes(hw.rootUINode, phaseDiff, shardsToUseForDiff); err != nil { - return hw, err - } - - if err := createUINodes(hw.rootUINode, phaseMigrateRdonly, sourceShards); err != nil { - return hw, err - } - if err := createUINodes(hw.rootUINode, phaseMigrateReplica, sourceShards); err != nil { - return hw, err - } - if err := createUINodes(hw.rootUINode, phaseMigrateMaster, sourceShards); err != nil { - return hw, err - } - - return hw, nil -} - -func createUINodes(rootNode *workflow.Node, phaseName workflow.PhaseType, shards []string) error { - phaseNode, err := rootNode.GetChildByPath(string(phaseName)) - if err != nil { - return fmt.Errorf("fails to find phase node for: %v", phaseName) - } - - for _, shard := range shards { - taskUINode := &workflow.Node{ - Name: "Shard " + shard, - PathName: shard, - } - phaseNode.Children = append(phaseNode.Children, taskUINode) - } - return nil -} - -// validateWorkflow validates that workflow has valid input parameters. -func validateWorkflow(m *workflow.Manager, keyspace string, vtworkers, sourceShards, destinationShards []string, minHealthyRdonlyTablets string, skipSplitRatioCheck bool) error { - if len(sourceShards) == 0 || len(destinationShards) == 0 { - return fmt.Errorf("invalid source or destination shards") - } - if len(vtworkers) != len(destinationShards) { - return fmt.Errorf("there are %v vtworkers, %v destination shards: the number should be same", len(vtworkers), len(destinationShards)) - } - - splitRatio := len(destinationShards) / len(sourceShards) - if minHealthyRdonlyTabletsVal, err := strconv.Atoi(minHealthyRdonlyTablets); err != nil || (!skipSplitRatioCheck && minHealthyRdonlyTabletsVal < splitRatio) { - return fmt.Errorf("there are not enough rdonly tablets in source shards. You need at least %v, it got: %v", splitRatio, minHealthyRdonlyTablets) - } - - // find the OverlappingShards in the keyspace - osList, err := topotools.FindOverlappingShards(context.Background(), m.TopoServer(), keyspace) - if err != nil { - return fmt.Errorf("cannot FindOverlappingShards in %v: %v", keyspace, err) - } - - // find the shard we mentioned in there, if any - os := topotools.OverlappingShardsForShard(osList, sourceShards[0]) - if os == nil { - return fmt.Errorf("the specified source shard %v/%v is not in any overlapping shard", keyspace, sourceShards[0]) - } - for _, sourceShard := range sourceShards { - if !os.ContainsShard(sourceShard) { - return fmt.Errorf("the specified source shard %v/%v is not in any overlapping shard", keyspace, sourceShard) - } - } - for _, destinationShard := range destinationShards { - if !os.ContainsShard(destinationShard) { - return fmt.Errorf("the specified destination shard %v/%v is not in any overlapping shard", keyspace, destinationShard) - } - } - return nil -} - -// initCheckpoint initialize the checkpoint for the horizontal workflow. -func initCheckpoint(keyspace string, vtworkers, excludeTables, sourceShards, destinationShards []string, minHealthyRdonlyTablets, splitCmd, splitDiffCmd, splitDiffDestTabletType string, useConsistentSnapshot string) (*workflowpb.WorkflowCheckpoint, error) { - tasks := make(map[string]*workflowpb.Task) - initTasks(tasks, phaseCopySchema, destinationShards, func(i int, shard string) map[string]string { - return map[string]string{ - "keyspace": keyspace, - "source_shard": sourceShards[0], - "destination_shard": shard, - "exclude_tables": strings.Join(excludeTables, ","), - } - }) - initTasks(tasks, phaseClone, sourceShards, func(i int, shard string) map[string]string { - return map[string]string{ - "keyspace": keyspace, - "source_shard": shard, - "min_healthy_rdonly_tablets": minHealthyRdonlyTablets, - "split_cmd": splitCmd, - "vtworker": vtworkers[i], - "use_consistent_snapshot": useConsistentSnapshot, - "exclude_tables": strings.Join(excludeTables, ","), - } - }) - initTasks(tasks, phaseWaitForFilteredReplication, destinationShards, func(i int, shard string) map[string]string { - return map[string]string{ - "keyspace": keyspace, - "destination_shard": shard, - } - }) - - switch splitDiffCmd { - case "SplitDiff": - initTasks(tasks, phaseDiff, destinationShards, func(i int, shard string) map[string]string { - return map[string]string{ - "keyspace": keyspace, - "destination_shard": shard, - "dest_tablet_type": splitDiffDestTabletType, - "split_diff_cmd": splitDiffCmd, - "vtworker": vtworkers[i], - "use_consistent_snapshot": useConsistentSnapshot, - "exclude_tables": strings.Join(excludeTables, ","), - } - }) - case "MultiSplitDiff": - initTasks(tasks, phaseDiff, sourceShards, func(i int, shard string) map[string]string { - return map[string]string{ - "keyspace": keyspace, - "source_shard": shard, - "dest_tablet_type": splitDiffDestTabletType, - "split_diff_cmd": splitDiffCmd, - "vtworker": vtworkers[i], - "use_consistent_snapshot": useConsistentSnapshot, - "exclude_tables": strings.Join(excludeTables, ","), - } - }) - } - - initTasks(tasks, phaseMigrateRdonly, sourceShards, func(i int, shard string) map[string]string { - return map[string]string{ - "keyspace": keyspace, - "source_shard": shard, - "served_type": topodatapb.TabletType_RDONLY.String(), - } - }) - initTasks(tasks, phaseMigrateReplica, sourceShards, func(i int, shard string) map[string]string { - return map[string]string{ - "keyspace": keyspace, - "source_shard": shard, - "served_type": topodatapb.TabletType_REPLICA.String(), - } - }) - initTasks(tasks, phaseMigrateMaster, sourceShards, func(i int, shard string) map[string]string { - return map[string]string{ - "keyspace": keyspace, - "source_shard": shard, - "served_type": topodatapb.TabletType_PRIMARY.String(), - } - }) - - return &workflowpb.WorkflowCheckpoint{ - CodeVersion: codeVersion, - Tasks: tasks, - Settings: map[string]string{ - "source_shards": strings.Join(sourceShards, ","), - "destination_shards": strings.Join(destinationShards, ","), - "split_diff_cmd": splitDiffCmd, - }, - }, nil -} - -func initTasks(tasks map[string]*workflowpb.Task, phase workflow.PhaseType, shards []string, getAttributes func(int, string) map[string]string) { - for i, shard := range shards { - taskID := createTaskID(phase, shard) - tasks[taskID] = &workflowpb.Task{ - Id: taskID, - State: workflowpb.TaskState_TaskNotStarted, - Attributes: getAttributes(i, shard), - } - } -} - -// horizontalReshardingWorkflow contains meta-information and methods to -// control the horizontal resharding workflow. -type horizontalReshardingWorkflow struct { - ctx context.Context - wr Wrangler - manager *workflow.Manager - topoServer *topo.Server - wi *topo.WorkflowInfo - // logger is the logger we export UI logs from. - logger *logutil.MemoryLogger - - // rootUINode is the root node representing the workflow in the UI. - rootUINode *workflow.Node - - checkpoint *workflowpb.WorkflowCheckpoint - checkpointWriter *workflow.CheckpointWriter - - phaseEnableApprovals map[string]bool -} - -// Run executes the horizontal resharding process. -// It implements the workflow.Workflow interface. -func (hw *horizontalReshardingWorkflow) Run(ctx context.Context, manager *workflow.Manager, wi *topo.WorkflowInfo) error { - hw.ctx = ctx - hw.wi = wi - hw.checkpointWriter = workflow.NewCheckpointWriter(hw.topoServer, hw.checkpoint, hw.wi) - hw.rootUINode.Display = workflow.NodeDisplayDeterminate - hw.rootUINode.BroadcastChanges(true /* updateChildren */) - - if err := hw.runWorkflow(); err != nil { - return err - } - hw.setUIMessage(fmt.Sprintf("Horizontal Resharding is finished successfully.")) //nolint - return nil -} - -func (hw *horizontalReshardingWorkflow) runWorkflow() error { - copySchemaTasks := hw.GetTasks(phaseCopySchema) - copySchemaRunner := workflow.NewParallelRunner(hw.ctx, hw.rootUINode, hw.checkpointWriter, copySchemaTasks, hw.runCopySchema, workflow.Parallel, hw.phaseEnableApprovals[string(phaseCopySchema)]) - if err := copySchemaRunner.Run(); err != nil { - return err - } - - cloneTasks := hw.GetTasks(phaseClone) - cloneRunner := workflow.NewParallelRunner(hw.ctx, hw.rootUINode, hw.checkpointWriter, cloneTasks, hw.runSplitClone, workflow.Parallel, hw.phaseEnableApprovals[string(phaseClone)]) - if err := cloneRunner.Run(); err != nil { - return err - } - - waitForFilteredReplicationTasks := hw.GetTasks(phaseWaitForFilteredReplication) - waitForFilteredReplicationRunner := workflow.NewParallelRunner(hw.ctx, hw.rootUINode, hw.checkpointWriter, waitForFilteredReplicationTasks, hw.runWaitForFilteredReplication, workflow.Parallel, hw.phaseEnableApprovals[string(phaseWaitForFilteredReplication)]) - if err := waitForFilteredReplicationRunner.Run(); err != nil { - return err - } - - diffTasks := hw.GetTasks(phaseDiff) - diffRunner := workflow.NewParallelRunner(hw.ctx, hw.rootUINode, hw.checkpointWriter, diffTasks, hw.runSplitDiff, workflow.Parallel, hw.phaseEnableApprovals[string(phaseWaitForFilteredReplication)]) - if err := diffRunner.Run(); err != nil { - return err - } - - migrateRdonlyTasks := hw.GetTasks(phaseMigrateRdonly) - migrateRdonlyRunner := workflow.NewParallelRunner(hw.ctx, hw.rootUINode, hw.checkpointWriter, migrateRdonlyTasks, hw.runMigrate, workflow.Sequential, hw.phaseEnableApprovals[string(phaseMigrateRdonly)]) - if err := migrateRdonlyRunner.Run(); err != nil { - return err - } - - migrateReplicaTasks := hw.GetTasks(phaseMigrateReplica) - migrateReplicaRunner := workflow.NewParallelRunner(hw.ctx, hw.rootUINode, hw.checkpointWriter, migrateReplicaTasks, hw.runMigrate, workflow.Sequential, hw.phaseEnableApprovals[string(phaseMigrateReplica)]) - if err := migrateReplicaRunner.Run(); err != nil { - return err - } - - migrateMasterTasks := hw.GetTasks(phaseMigrateMaster) - migrateMasterRunner := workflow.NewParallelRunner(hw.ctx, hw.rootUINode, hw.checkpointWriter, migrateMasterTasks, hw.runMigrate, workflow.Sequential, hw.phaseEnableApprovals[string(phaseMigrateReplica)]) - return migrateMasterRunner.Run() -} - -func (hw *horizontalReshardingWorkflow) setUIMessage(message string) { - log.Infof("Horizontal resharding : %v.", message) - hw.logger.Infof(message) - hw.rootUINode.Log = hw.logger.String() - hw.rootUINode.Message = message - hw.rootUINode.BroadcastChanges(false /* updateChildren */) -} - -// WorkflowPhases returns phases for resharding workflow -func WorkflowPhases() []string { - return []string{ - string(phaseCopySchema), - string(phaseClone), - string(phaseWaitForFilteredReplication), - string(phaseDiff), - string(phaseMigrateReplica), - string(phaseMigrateRdonly), - string(phaseMigrateMaster), - } -} - -func parsePhaseEnableApprovals(phaseEnableApprovalsStr string) []string { - var phaseEnableApprovals []string - if phaseEnableApprovalsStr == "" { - return phaseEnableApprovals - } - phaseEnableApprovals = strings.Split(phaseEnableApprovalsStr, ",") - for i, phase := range phaseEnableApprovals { - phaseEnableApprovals[i] = strings.Trim(phase, " ") - } - return phaseEnableApprovals -} diff --git a/go/vt/workflow/resharding/workflow_test.go b/go/vt/workflow/resharding/workflow_test.go deleted file mode 100644 index a1e79bf5a04..00000000000 --- a/go/vt/workflow/resharding/workflow_test.go +++ /dev/null @@ -1,247 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package resharding - -import ( - "flag" - "testing" - - "context" - - "github.com/golang/mock/gomock" - - "vitess.io/vitess/go/vt/topo" - "vitess.io/vitess/go/vt/topo/memorytopo" - "vitess.io/vitess/go/vt/worker/fakevtworkerclient" - "vitess.io/vitess/go/vt/worker/vtworkerclient" - "vitess.io/vitess/go/vt/workflow" - "vitess.io/vitess/go/vt/wrangler" - - // import the gRPC client implementation for tablet manager - _ "vitess.io/vitess/go/vt/vttablet/grpctmclient" - - topodatapb "vitess.io/vitess/go/vt/proto/topodata" -) - -var ( - testKeyspace = "test_keyspace" - testVtworkers = "localhost:15032" -) - -func init() { - Register() -} - -// TestSourceDestShards tests that provided source/dest shards are valid -func TestSourceDestShards(t *testing.T) { - ctx := context.Background() - - // Set up the mock wrangler. It is used for the CopySchema, - // WaitforFilteredReplication and Migrate phase. - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - // Set up the fakeworkerclient. It is used at SplitClone and SplitDiff phase. - fakeVtworkerClient := setupFakeVtworker(testKeyspace, testVtworkers, false, "", "SplitDiff") - vtworkerclient.RegisterFactory("fake", fakeVtworkerClient.FakeVtworkerClientFactory) - defer vtworkerclient.UnregisterFactoryForTest("fake") - - // Initialize the topology. - ts := setupTopology(ctx, t, testKeyspace) - m := workflow.NewManager(ts) - // Run the manager in the background. - _, _, cancel := workflow.StartManager(m) - // Create the workflow. - vtworkersParameter := testVtworkers + "," + testVtworkers - _, err := m.Create(ctx, horizontalReshardingFactoryName, []string{"-keyspace=" + testKeyspace, "-vtworkers=" + vtworkersParameter, "-phase_enable_approvals=", "-min_healthy_rdonly_tablets=2", "-source_shards=0", "-destination_shards=-40,40-"}) - want := "the specified destination shard test_keyspace/-40 is not in any overlapping shard" - if err == nil || err.Error() != want { - t.Errorf("workflow error: %v, want %s", err, want) - } - - _, err = m.Create(ctx, horizontalReshardingFactoryName, []string{"-keyspace=" + testKeyspace, "-vtworkers=" + vtworkersParameter, "-phase_enable_approvals=", "-min_healthy_rdonly_tablets=2", "-source_shards=0", "-destination_shards=-80,40-"}) - - want = "the specified destination shard test_keyspace/40- is not in any overlapping shard" - if err == nil || err.Error() != want { - t.Errorf("workflow error: %v, want %s", err, want) - } - - _, err = m.Create(ctx, horizontalReshardingFactoryName, []string{"-keyspace=" + testKeyspace, "-vtworkers=" + vtworkersParameter, "-phase_enable_approvals=", "-min_healthy_rdonly_tablets=2", "-source_shards=-20", "-destination_shards=-80,80-"}) - - want = "the specified source shard test_keyspace/-20 is not in any overlapping shard" - if err == nil || err.Error() != want { - t.Errorf("workflow error: %v, want %s", err, want) - } - cancel() -} - -// TestHorizontalResharding runs the happy path of HorizontalReshardingWorkflow. -func TestHorizontalResharding(t *testing.T) { - testHorizontalReshardingWorkflow(t, false, "", "SplitDiff") -} - -// TestHorizontalReshardingWithConsistentSnapshot runs the happy path of HorizontalReshardingWorkflow with consistent snapshot. -func TestHorizontalReshardingWithConsistentSnapshot(t *testing.T) { - testHorizontalReshardingWorkflow(t, true, "", "SplitDiff") -} - -// TestHorizontalReshardingWithExcludedTables runs the happy path of HorizontalReshardingWorkflow with excluded tables. -func TestHorizontalReshardingWithExcludedTables(t *testing.T) { - testHorizontalReshardingWorkflow(t, true, "table_a,table_b", "SplitDiff") -} - -func TestHorizontalReshardingWithMultiDiffCommand(t *testing.T) { - testHorizontalReshardingWorkflow(t, true, "table_a,table_b", "MultiSplitDiff") -} - -func testHorizontalReshardingWorkflow(t *testing.T, useConsistentSnapshot bool, excludeTables, splitDiffCommand string) { - ctx := context.Background() - // Set up the mock wrangler. It is used for the CopySchema, - // WaitforFilteredReplication and Migrate phase. - ctrl := gomock.NewController(t) - defer ctrl.Finish() - mockWranglerInterface := setupMockWrangler(ctrl, testKeyspace) - // Set up the fakeworkerclient. It is used at SplitClone and SplitDiff phase. - fakeVtworkerClient := setupFakeVtworker(testKeyspace, testVtworkers, useConsistentSnapshot, excludeTables, splitDiffCommand) - vtworkerclient.RegisterFactory("fake", fakeVtworkerClient.FakeVtworkerClientFactory) - defer vtworkerclient.UnregisterFactoryForTest("fake") - // Initialize the topology. - ts := setupTopology(ctx, t, testKeyspace) - m := workflow.NewManager(ts) - // Run the manager in the background. - wg, _, cancel := workflow.StartManager(m) - // Create the workflow. - vtworkersParameter := testVtworkers + "," + testVtworkers - args := []string{"-keyspace=" + testKeyspace, "-vtworkers=" + vtworkersParameter, "-phase_enable_approvals=", "-min_healthy_rdonly_tablets=2"} - if useConsistentSnapshot { - args = append(args, "-use_consistent_snapshot") - } - if excludeTables != "" { - args = append(args, "-exclude_tables="+excludeTables) - } - args = append(args, "-source_shards=0", "-destination_shards=-80,80-", "-split_diff_cmd="+splitDiffCommand) - uuid, err := m.Create(ctx, horizontalReshardingFactoryName, args) - if err != nil { - t.Fatalf("cannot create resharding workflow: %v", err) - } - // Inject the mock wranger into the workflow. - w, err := m.WorkflowForTesting(uuid) - if err != nil { - t.Fatalf("fail to get workflow from manager: %v", err) - } - hw := w.(*horizontalReshardingWorkflow) - hw.wr = mockWranglerInterface - // Start the job. - if err := m.Start(ctx, uuid); err != nil { - t.Fatalf("cannot start resharding workflow: %v", err) - } - // Wait for the workflow to end. - m.Wait(ctx, uuid) - if err := workflow.VerifyAllTasksDone(ctx, ts, uuid); err != nil { - t.Fatal(err) - } - // Stop the manager. - if err := m.Stop(ctx, uuid); err != nil { - t.Fatalf("cannot stop resharding workflow: %v", err) - } - cancel() - wg.Wait() -} - -func setupFakeVtworker(keyspace, vtworkers string, useConsistentSnapshot bool, excludeTables, splitDiffCmd string) *fakevtworkerclient.FakeVtworkerClient { - flag.Set("vtworker_client_protocol", "fake") - fakeVtworkerClient := fakevtworkerclient.NewFakeVtworkerClient() - fakeVtworkerClient.RegisterResultForAddr(vtworkers, resetCommand(), "", nil) - fakeVtworkerClient.RegisterResultForAddr(vtworkers, splitCloneCommand(keyspace, useConsistentSnapshot, excludeTables), "", nil) - fakeVtworkerClient.RegisterResultForAddr(vtworkers, resetCommand(), "", nil) - fakeVtworkerClient.RegisterResultForAddr(vtworkers, resetCommand(), "", nil) - - switch splitDiffCmd { - case "SplitDiff": - fakeVtworkerClient.RegisterResultForAddr(vtworkers, splitDiffCommand(keyspace, "-80", useConsistentSnapshot, excludeTables, splitDiffCmd), "", nil) - fakeVtworkerClient.RegisterResultForAddr(vtworkers, splitDiffCommand(keyspace, "80-", useConsistentSnapshot, excludeTables, splitDiffCmd), "", nil) - case "MultiSplitDiff": - fakeVtworkerClient.RegisterResultForAddr(vtworkers, splitDiffCommand(keyspace, "0", useConsistentSnapshot, excludeTables, splitDiffCmd), "", nil) - fakeVtworkerClient.RegisterResultForAddr(vtworkers, splitDiffCommand(keyspace, "0", useConsistentSnapshot, excludeTables, splitDiffCmd), "", nil) - } - return fakeVtworkerClient -} - -func resetCommand() []string { - return []string{"Reset"} -} - -func splitCloneCommand(keyspace string, useConsistentSnapshot bool, excludeTables string) []string { - args := []string{"SplitClone", "--min_healthy_rdonly_tablets=2"} - if useConsistentSnapshot { - args = append(args, "--use_consistent_snapshot") - } - if excludeTables != "" { - args = append(args, "--exclude_tables="+excludeTables) - } - - args = append(args, keyspace+"/0") - return args -} - -func splitDiffCommand(keyspace string, shardID string, useConsistentSnapshot bool, excludeTables, splitDiffCommand string) []string { - args := []string{splitDiffCommand} - if useConsistentSnapshot { - args = append(args, "--use_consistent_snapshot") - } - if excludeTables != "" { - args = append(args, "--exclude_tables="+excludeTables) - } - - switch splitDiffCommand { - case "SplitDiff": - args = append(args, "--min_healthy_rdonly_tablets=1", "--dest_tablet_type=RDONLY", keyspace+"/"+shardID) - case "MultiSplitDiff": - args = append(args, "--min_healthy_tablets=1", "--tablet_type=RDONLY", keyspace+"/"+shardID) - } - - return args -} - -func setupMockWrangler(ctrl *gomock.Controller, keyspace string) *MockReshardingWrangler { - mockWranglerInterface := NewMockReshardingWrangler(ctrl) - // Set the expected behaviors for mock wrangler. - mockWranglerInterface.EXPECT().CopySchemaShardFromShard(gomock.Any(), nil /* tableArray*/, gomock.Any() /* excludeTableArray */, true /*includeViews*/, keyspace, "0", keyspace, "-80", wrangler.DefaultWaitReplicasTimeout, false).Return(nil) - mockWranglerInterface.EXPECT().CopySchemaShardFromShard(gomock.Any(), nil /* tableArray*/, gomock.Any() /* excludeTableArray */, true /*includeViews*/, keyspace, "0", keyspace, "80-", wrangler.DefaultWaitReplicasTimeout, false).Return(nil) - - mockWranglerInterface.EXPECT().WaitForFilteredReplication(gomock.Any(), keyspace, "-80", wrangler.DefaultWaitForFilteredReplicationMaxDelay).Return(nil) - mockWranglerInterface.EXPECT().WaitForFilteredReplication(gomock.Any(), keyspace, "80-", wrangler.DefaultWaitForFilteredReplicationMaxDelay).Return(nil) - - servedTypeParams := []topodatapb.TabletType{topodatapb.TabletType_RDONLY, - topodatapb.TabletType_REPLICA, - topodatapb.TabletType_PRIMARY} - for _, servedType := range servedTypeParams { - mockWranglerInterface.EXPECT().MigrateServedTypes(gomock.Any(), keyspace, "0", nil /* cells */, servedType, false /* reverse */, false /* skipReFreshState */, wrangler.DefaultFilteredReplicationWaitTime, false /* reverseReplication */).Return(nil) - } - return mockWranglerInterface -} - -func setupTopology(ctx context.Context, t *testing.T, keyspace string) *topo.Server { - ts := memorytopo.NewServer("cell") - if err := ts.CreateKeyspace(ctx, keyspace, &topodatapb.Keyspace{}); err != nil { - t.Fatalf("CreateKeyspace: %v", err) - } - ts.CreateShard(ctx, keyspace, "0") - ts.CreateShard(ctx, keyspace, "-80") - ts.CreateShard(ctx, keyspace, "80-") - return ts -} diff --git a/go/vt/workflow/reshardingworkflowgen/workflow.go b/go/vt/workflow/reshardingworkflowgen/workflow.go deleted file mode 100644 index f3c751edbe5..00000000000 --- a/go/vt/workflow/reshardingworkflowgen/workflow.go +++ /dev/null @@ -1,371 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package reshardingworkflowgen - -// This package contains a workflow to generate horizontal resharding workflows -// that automatically discovers available overlapping shards to split/merge. - -import ( - "flag" - "fmt" - "path" - "strconv" - "strings" - - "google.golang.org/protobuf/proto" - - "context" - - "vitess.io/vitess/go/vt/log" - "vitess.io/vitess/go/vt/logutil" - "vitess.io/vitess/go/vt/topo" - "vitess.io/vitess/go/vt/topotools" - "vitess.io/vitess/go/vt/workflow" - "vitess.io/vitess/go/vt/workflow/resharding" - - workflowpb "vitess.io/vitess/go/vt/proto/workflow" -) - -const ( - codeVersion = 1 - - keyspaceReshardingFactoryName = "hr_workflow_gen" - phaseName = "create_workflows" -) - -// Register registers the KeyspaceResharding as a factory -// in the workflow framework. -func Register() { - workflow.Register(keyspaceReshardingFactoryName, &Factory{}) -} - -// Factory is the factory to create -// a horizontal resharding workflow. -type Factory struct{} - -// Init is part of the workflow.Factory interface. -func (*Factory) Init(m *workflow.Manager, w *workflowpb.Workflow, args []string) error { - subFlags := flag.NewFlagSet(keyspaceReshardingFactoryName, flag.ContinueOnError) - keyspace := subFlags.String("keyspace", "", "Name of keyspace to perform horizontal resharding") - vtworkersStr := subFlags.String("vtworkers", "", "A comma-separated list of vtworker addresses") - excludeTablesStr := subFlags.String("exclude_tables", "", "A comma-separated list of tables to exclude") - minHealthyRdonlyTablets := subFlags.String("min_healthy_rdonly_tablets", "1", "Minimum number of healthy RDONLY tablets required in source shards") - skipSplitRatioCheck := subFlags.Bool("skip_split_ratio_check", false, "Skip validation on minimum number of healthy RDONLY tablets") - splitCmd := subFlags.String("split_cmd", "SplitClone", "Split command to use to perform horizontal resharding") - splitDiffCmd := subFlags.String("split_diff_cmd", "SplitDiff", "Split diff command to use to perform horizontal resharding (either SplitDiff or MultiSplitDiff)") - splitDiffDestTabletType := subFlags.String("split_diff_dest_tablet_type", "RDONLY", "Specifies tablet type to use in destination shards while performing SplitDiff operation") - skipStartWorkflows := subFlags.Bool("skip_start_workflows", true, "If true, newly created workflows will have skip_start set") - phaseEnableApprovalsDesc := fmt.Sprintf("Comma separated phases that require explicit approval in the UI to execute. Phase names are: %v", strings.Join(resharding.WorkflowPhases(), ",")) - phaseEnableApprovalsStr := subFlags.String("phase_enable_approvals", strings.Join(resharding.WorkflowPhases(), ","), phaseEnableApprovalsDesc) - useConsistentSnapshot := subFlags.Bool("use_consistent_snapshot", false, "Instead of pausing replication on the source, uses transactions with consistent snapshot to have a stable view of the data.") - - if err := subFlags.Parse(args); err != nil { - return err - } - if *keyspace == "" || *vtworkersStr == "" || *minHealthyRdonlyTablets == "" || *splitCmd == "" || *splitDiffCmd == "" { - return fmt.Errorf("keyspace name, min healthy rdonly tablets, split command, and vtworkers information must be provided for horizontal resharding") - } - - vtworkers := strings.Split(*vtworkersStr, ",") - excludeTables := strings.Split(*excludeTablesStr, ",") - - w.Name = fmt.Sprintf("Keyspace reshard on %s", *keyspace) - shardsToSplit, err := findSourceAndDestinationShards(m.TopoServer(), *keyspace) - if err != nil { - return err - } - - checkpoint, err := initCheckpoint( - *keyspace, - vtworkers, - excludeTables, - shardsToSplit, - *minHealthyRdonlyTablets, - *splitCmd, - *splitDiffCmd, - *splitDiffDestTabletType, - *phaseEnableApprovalsStr, - *skipStartWorkflows, - *useConsistentSnapshot, - *skipSplitRatioCheck, - ) - if err != nil { - return err - } - - w.Data, err = proto.Marshal(checkpoint) - if err != nil { - return err - } - return nil -} - -// Instantiate is part the workflow.Factory interface. -func (*Factory) Instantiate(m *workflow.Manager, w *workflowpb.Workflow, rootNode *workflow.Node) (workflow.Workflow, error) { - rootNode.Message = "This is a workflow to execute a keyspace resharding automatically." - - checkpoint := &workflowpb.WorkflowCheckpoint{} - if err := proto.Unmarshal(w.Data, checkpoint); err != nil { - return nil, err - } - - workflowsCount, err := strconv.Atoi(checkpoint.Settings["workflows_count"]) - if err != nil { - return nil, err - } - - hw := &reshardingWorkflowGen{ - checkpoint: checkpoint, - rootUINode: rootNode, - logger: logutil.NewMemoryLogger(), - topoServer: m.TopoServer(), - manager: m, - phaseEnableApprovalsParam: checkpoint.Settings["phase_enable_approvals"], - skipStartWorkflowParam: checkpoint.Settings["skip_start_workflows"], - minHealthyRdonlyTabletsParam: checkpoint.Settings["min_healthy_rdonly_tablets"], - keyspaceParam: checkpoint.Settings["keyspace"], - splitDiffDestTabletTypeParam: checkpoint.Settings["split_diff_dest_tablet_type"], - splitCmdParam: checkpoint.Settings["split_cmd"], - splitDiffCmdParam: checkpoint.Settings["split_diff_cmd"], - useConsistentSnapshot: checkpoint.Settings["use_consistent_snapshot"], - excludeTablesParam: checkpoint.Settings["exclude_tables"], - skipSplitRatioCheckParam: checkpoint.Settings["skip_split_ratio_check"], - workflowsCount: workflowsCount, - } - createWorkflowsUINode := &workflow.Node{ - Name: "CreateWorkflows", - PathName: phaseName, - } - hw.rootUINode.Children = []*workflow.Node{ - createWorkflowsUINode, - } - - phaseNode, err := rootNode.GetChildByPath(phaseName) - if err != nil { - return nil, fmt.Errorf("fails to find phase node for: %v", phaseName) - } - - for i := 0; i < workflowsCount; i++ { - taskID := fmt.Sprintf("%s/%v", phaseName, i) - task := hw.checkpoint.Tasks[taskID] - taskUINode := &workflow.Node{ - Name: fmt.Sprintf("Split shards %v to %v workflow creation", task.Attributes["source_shards"], task.Attributes["destination_shards"]), - PathName: fmt.Sprintf("%v", i), - } - phaseNode.Children = append(phaseNode.Children, taskUINode) - } - return hw, nil -} - -func findSourceAndDestinationShards(ts *topo.Server, keyspace string) ([][][]string, error) { - overlappingShards, err := topotools.FindOverlappingShards(context.Background(), ts, keyspace) - if err != nil { - return nil, err - } - - var shardsToSplit [][][]string - - for _, os := range overlappingShards { - var sourceShards, destinationShards []string - var sourceShardInfo *topo.ShardInfo - var destinationShardInfos []*topo.ShardInfo - - isLeftServing := os.Left[0].IsPrimaryServing - if err != nil { - return nil, err - } - if isLeftServing { - sourceShardInfo = os.Left[0] - destinationShardInfos = os.Right - } else { - sourceShardInfo = os.Right[0] - destinationShardInfos = os.Left - } - sourceShards = append(sourceShards, sourceShardInfo.ShardName()) - for _, d := range destinationShardInfos { - destinationShards = append(destinationShards, d.ShardName()) - } - shardsToSplit = append(shardsToSplit, [][]string{sourceShards, destinationShards}) - } - return shardsToSplit, nil -} - -// initCheckpoint initialize the checkpoint for keyspace reshard -func initCheckpoint(keyspace string, vtworkers, excludeTables []string, shardsToSplit [][][]string, minHealthyRdonlyTablets, splitCmd, splitDiffCmd, splitDiffDestTabletType, phaseEnableApprovals string, skipStartWorkflows, useConsistentSnapshot, skipSplitRatioCheck bool) (*workflowpb.WorkflowCheckpoint, error) { - sourceShards := 0 - destShards := 0 - for _, shardToSplit := range shardsToSplit { - sourceShards = sourceShards + len(shardToSplit[0]) - destShards = destShards + len(shardToSplit[1]) - } - if sourceShards == 0 || destShards == 0 { - return nil, fmt.Errorf("invalid source or destination shards") - } - if len(vtworkers) != destShards { - return nil, fmt.Errorf("there are %v vtworkers, %v destination shards: the number should be same", len(vtworkers), destShards) - } - - splitRatio := destShards / sourceShards - if minHealthyRdonlyTabletsVal, err := strconv.Atoi(minHealthyRdonlyTablets); err != nil || (!skipSplitRatioCheck && minHealthyRdonlyTabletsVal < splitRatio) { - return nil, fmt.Errorf("there are not enough rdonly tablets in source shards. You need at least %v, it got: %v", splitRatio, minHealthyRdonlyTablets) - } - - tasks := make(map[string]*workflowpb.Task) - usedVtworkersIdx := 0 - for i, shardToSplit := range shardsToSplit { - taskID := fmt.Sprintf("%s/%v", phaseName, i) - tasks[taskID] = &workflowpb.Task{ - Id: taskID, - State: workflowpb.TaskState_TaskNotStarted, - Attributes: map[string]string{ - "source_shards": strings.Join(shardToSplit[0], ","), - "destination_shards": strings.Join(shardToSplit[1], ","), - "vtworkers": strings.Join(vtworkers[usedVtworkersIdx:usedVtworkersIdx+len(shardToSplit[1])], ","), - }, - } - usedVtworkersIdx = usedVtworkersIdx + len(shardToSplit[1]) - } - return &workflowpb.WorkflowCheckpoint{ - CodeVersion: codeVersion, - Tasks: tasks, - Settings: map[string]string{ - "vtworkers": strings.Join(vtworkers, ","), - "min_healthy_rdonly_tablets": minHealthyRdonlyTablets, - "split_cmd": splitCmd, - "split_diff_cmd": splitDiffCmd, - "split_diff_dest_tablet_type": splitDiffDestTabletType, - "phase_enable_approvals": phaseEnableApprovals, - "skip_start_workflows": fmt.Sprintf("%v", skipStartWorkflows), - "workflows_count": fmt.Sprintf("%v", len(shardsToSplit)), - "keyspace": keyspace, - "use_consistent_snapshot": fmt.Sprintf("%v", useConsistentSnapshot), - "exclude_tables": strings.Join(excludeTables, ","), - "skip_split_ratio_check": fmt.Sprintf("%v", skipSplitRatioCheck), - }, - }, nil -} - -// reshardingWorkflowGen contains meta-information and methods to -// control workflow. -type reshardingWorkflowGen struct { - ctx context.Context - manager *workflow.Manager - topoServer *topo.Server - wi *topo.WorkflowInfo - // logger is the logger we export UI logs from. - logger *logutil.MemoryLogger - - // rootUINode is the root node representing the workflow in the UI. - rootUINode *workflow.Node - - checkpoint *workflowpb.WorkflowCheckpoint - checkpointWriter *workflow.CheckpointWriter - - workflowsCount int - - // params to horizontal reshard workflow - phaseEnableApprovalsParam string - minHealthyRdonlyTabletsParam string - keyspaceParam string - splitDiffDestTabletTypeParam string - splitCmdParam string - splitDiffCmdParam string - skipStartWorkflowParam string - useConsistentSnapshot string - excludeTablesParam string - skipSplitRatioCheckParam string -} - -// Run implements workflow.Workflow interface. It creates one horizontal resharding workflow per shard to split -func (hw *reshardingWorkflowGen) Run(ctx context.Context, manager *workflow.Manager, wi *topo.WorkflowInfo) error { - hw.ctx = ctx - hw.wi = wi - hw.checkpointWriter = workflow.NewCheckpointWriter(hw.topoServer, hw.checkpoint, hw.wi) - hw.rootUINode.Display = workflow.NodeDisplayDeterminate - hw.rootUINode.BroadcastChanges(true /* updateChildren */) - - if err := hw.runWorkflow(); err != nil { - hw.setUIMessage(hw.rootUINode, fmt.Sprintf("Keyspace resharding failed to create workflows")) //nolint - return err - } - hw.setUIMessage(hw.rootUINode, fmt.Sprintf("Keyspace resharding is finished successfully.")) //nolint - return nil -} - -func (hw *reshardingWorkflowGen) runWorkflow() error { - var tasks []*workflowpb.Task - for i := 0; i < hw.workflowsCount; i++ { - taskID := fmt.Sprintf("%s/%v", phaseName, i) - tasks = append(tasks, hw.checkpoint.Tasks[taskID]) - } - - workflowsCreator := workflow.NewParallelRunner(hw.ctx, hw.rootUINode, hw.checkpointWriter, tasks, hw.workflowCreator, workflow.Sequential, false /*phaseEnableApprovals we don't require enable approvals in this workflow*/) - return workflowsCreator.Run() -} - -func (hw *reshardingWorkflowGen) workflowCreator(ctx context.Context, task *workflowpb.Task) error { - horizontalReshardingParams := []string{ - "-keyspace=" + hw.keyspaceParam, - "-vtworkers=" + task.Attributes["vtworkers"], - "-exclude_tables=" + hw.excludeTablesParam, - "-skip_split_ratio_check=" + hw.skipSplitRatioCheckParam, - "-split_cmd=" + hw.splitCmdParam, - "-split_diff_cmd=" + hw.splitDiffCmdParam, - "-split_diff_dest_tablet_type=" + hw.splitDiffDestTabletTypeParam, - "-min_healthy_rdonly_tablets=" + hw.minHealthyRdonlyTabletsParam, - "-source_shards=" + task.Attributes["source_shards"], - "-destination_shards=" + task.Attributes["destination_shards"], - "-phase_enable_approvals=" + hw.phaseEnableApprovalsParam, - "-use_consistent_snapshot=" + hw.useConsistentSnapshot, - } - - skipStart, err := strconv.ParseBool(hw.skipStartWorkflowParam) - if err != nil { - return err - - } - phaseID := path.Dir(task.Id) - phaseUINode, err := hw.rootUINode.GetChildByPath(phaseID) - if err != nil { - return err - } - - uuid, err := hw.manager.Create(ctx, "horizontal_resharding", horizontalReshardingParams) - if err != nil { - hw.setUIMessage(phaseUINode, fmt.Sprintf("Couldn't create shard split workflow for source shards: %v. Got error: %v", task.Attributes["source_shards"], err)) - return err - } - hw.setUIMessage(phaseUINode, fmt.Sprintf("Created shard split workflow: %v for source shards: %v.", uuid, task.Attributes["source_shards"])) - workflowCmd := "WorkflowCreate horizontal_resharding" + strings.Join(horizontalReshardingParams, " ") - hw.setUIMessage(phaseUINode, fmt.Sprintf("Created workflow with the following params: %v", workflowCmd)) - if !skipStart { - err = hw.manager.Start(ctx, uuid) - if err != nil { - hw.setUIMessage(phaseUINode, fmt.Sprintf("Couldn't start shard split workflow: %v for source shards: %v. Got error: %v", uuid, task.Attributes["source_shards"], err)) - return err - } - } - return nil -} - -func (hw *reshardingWorkflowGen) setUIMessage(node *workflow.Node, message string) { - log.Infof("Keyspace resharding : %v.", message) - hw.logger.Infof(message) - node.Log = hw.logger.String() - node.Message = message - node.BroadcastChanges(false /* updateChildren */) -} diff --git a/go/vt/workflow/reshardingworkflowgen/workflow_test.go b/go/vt/workflow/reshardingworkflowgen/workflow_test.go deleted file mode 100644 index b3627dad594..00000000000 --- a/go/vt/workflow/reshardingworkflowgen/workflow_test.go +++ /dev/null @@ -1,86 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package reshardingworkflowgen - -import ( - "testing" - - "context" - - "vitess.io/vitess/go/vt/topo" - "vitess.io/vitess/go/vt/topo/memorytopo" - "vitess.io/vitess/go/vt/workflow" - "vitess.io/vitess/go/vt/workflow/resharding" - - // import the gRPC client implementation for tablet manager - _ "vitess.io/vitess/go/vt/vttablet/grpctmclient" - - topodatapb "vitess.io/vitess/go/vt/proto/topodata" -) - -var ( - testKeyspace = "test_keyspace" - testVtworkers = "localhost:15032" -) - -func init() { - Register() - resharding.Register() -} - -// TestWorkflowGenerator runs the happy path of HorizontalReshardingWorkflow. -func TestWorkflowGenerator(t *testing.T) { - ctx := context.Background() - - // Initialize the topology. - ts := setupTopology(ctx, t, testKeyspace) - m := workflow.NewManager(ts) - // Run the manager in the background. - workflow.StartManager(m) - // Create the workflow. - vtworkersParameter := testVtworkers + "," + testVtworkers - uuid, err := m.Create(ctx, keyspaceReshardingFactoryName, []string{"-keyspace=" + testKeyspace, "-vtworkers=" + vtworkersParameter, "-min_healthy_rdonly_tablets=2", "-use_consistent_snapshot=true", "-exclude_tables=table_a,table_b"}) - if err != nil { - t.Fatalf("cannot create resharding workflow: %v", err) - } - - // Start the job. - if err := m.Start(ctx, uuid); err != nil { - t.Fatalf("cannot start resharding workflow: %v", err) - } - - // Wait for the workflow to end. - m.Wait(ctx, uuid) - if err := workflow.VerifyAllTasksDone(ctx, ts, uuid); err != nil { - t.Fatal(err) - } - // Stop the manager. - if err := m.Stop(ctx, uuid); err != nil { - t.Fatalf("cannot stop resharding workflow: %v", err) - } -} - -func setupTopology(ctx context.Context, t *testing.T, keyspace string) *topo.Server { - ts := memorytopo.NewServer("cell") - if err := ts.CreateKeyspace(ctx, keyspace, &topodatapb.Keyspace{}); err != nil { - t.Fatalf("CreateKeyspace: %v", err) - } - ts.CreateShard(ctx, keyspace, "0") - ts.CreateShard(ctx, keyspace, "-80") - ts.CreateShard(ctx, keyspace, "80-") - return ts -} diff --git a/go/vt/wrangler/keyspace.go b/go/vt/wrangler/keyspace.go index e8cd498fe8f..5420ec32102 100644 --- a/go/vt/wrangler/keyspace.go +++ b/go/vt/wrangler/keyspace.go @@ -19,24 +19,17 @@ package wrangler import ( "bytes" "context" - "flag" "fmt" "strings" "sync" "time" - "vitess.io/vitess/go/event" "vitess.io/vitess/go/sqltypes" - "vitess.io/vitess/go/vt/binlog/binlogplayer" "vitess.io/vitess/go/vt/concurrency" - "vitess.io/vitess/go/vt/discovery" - "vitess.io/vitess/go/vt/key" - binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" 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/topotools" - "vitess.io/vitess/go/vt/topotools/events" "vitess.io/vitess/go/vt/vterrors" ) @@ -45,12 +38,6 @@ const ( DefaultFilteredReplicationWaitTime = 30 * time.Second ) -// TODO(b/26388813): Remove these flags once vtctl WaitForDrain is integrated in the vtctl MigrateServed* commands. -var ( - waitForDrainSleepRdonly = flag.Duration("wait_for_drain_sleep_rdonly", 5*time.Second, "(DEPRECATED) time to wait before shutting the query service on old RDONLY tablets during MigrateServedTypes") - waitForDrainSleepReplica = flag.Duration("wait_for_drain_sleep_replica", 15*time.Second, "(DEPRECATED) time to wait before shutting the query service on old REPLICA tablets during MigrateServedTypes") -) - // keyspace related methods for Wrangler // validateNewWorkflow ensures that the specified workflow doesn't already exist @@ -104,143 +91,6 @@ func (wr *Wrangler) validateNewWorkflow(ctx context.Context, keyspace, workflow return allErrors.AggrError(vterrors.Aggregate) } -// SplitClone initiates a SplitClone workflow. -func (wr *Wrangler) SplitClone(ctx context.Context, keyspace string, from, to []string) error { - var fromShards, toShards []*topo.ShardInfo - for _, shard := range from { - si, err := wr.ts.GetShard(ctx, keyspace, shard) - if err != nil { - return vterrors.Wrapf(err, "GetShard(%s) failed", shard) - } - fromShards = append(fromShards, si) - } - for _, shard := range to { - si, err := wr.ts.GetShard(ctx, keyspace, shard) - if err != nil { - return vterrors.Wrapf(err, "GetShard(%s) failed", shard) - } - toShards = append(toShards, si) - } - // TODO(sougou): validate from and to shards. - - for _, dest := range toShards { - primary, err := wr.ts.GetTablet(ctx, dest.PrimaryAlias) - if err != nil { - return vterrors.Wrapf(err, "GetTablet(%v) failed", dest.PrimaryAlias) - } - var ids []uint64 - for _, source := range fromShards { - filter := &binlogdatapb.Filter{ - Rules: []*binlogdatapb.Rule{{ - Match: "/.*", - Filter: key.KeyRangeString(dest.KeyRange), - }}, - } - bls := &binlogdatapb.BinlogSource{ - Keyspace: keyspace, - Shard: source.ShardName(), - Filter: filter, - } - cmd := binlogplayer.CreateVReplicationState("VSplitClone", bls, "", binlogplayer.BlpStopped, primary.DbName()) - qr, err := wr.TabletManagerClient().VReplicationExec(ctx, primary.Tablet, cmd) - if err != nil { - return vterrors.Wrapf(err, "VReplicationExec(%v, %s) failed", dest.PrimaryAlias, cmd) - } - if err := wr.SourceShardAdd(ctx, keyspace, dest.ShardName(), uint32(qr.InsertId), keyspace, source.ShardName(), source.Shard.KeyRange, nil); err != nil { - return vterrors.Wrapf(err, "SourceShardAdd(%s, %s) failed", dest.ShardName(), source.ShardName()) - } - ids = append(ids, qr.InsertId) - } - // Start vreplication only if all metadata was successfully created. - for _, id := range ids { - cmd := fmt.Sprintf("update _vt.vreplication set state='%s' where id=%d", binlogplayer.VReplicationInit, id) - if _, err = wr.TabletManagerClient().VReplicationExec(ctx, primary.Tablet, cmd); err != nil { - return vterrors.Wrapf(err, "VReplicationExec(%v, %s) failed", dest.PrimaryAlias, cmd) - } - } - } - return wr.refreshPrimaryTablets(ctx, toShards) -} - -// VerticalSplitClone initiates a VerticalSplitClone workflow. -func (wr *Wrangler) VerticalSplitClone(ctx context.Context, fromKeyspace, toKeyspace string, tables []string) error { - source, err := wr.ts.GetOnlyShard(ctx, fromKeyspace) - if err != nil { - return vterrors.Wrapf(err, "GetOnlyShard(%s) failed", fromKeyspace) - } - dest, err := wr.ts.GetOnlyShard(ctx, toKeyspace) - if err != nil { - return vterrors.Wrapf(err, "GetOnlyShard(%s) failed", toKeyspace) - } - // TODO(sougou): validate from and to shards. - - primary, err := wr.ts.GetTablet(ctx, dest.PrimaryAlias) - if err != nil { - return vterrors.Wrapf(err, "GetTablet(%v) failed", dest.PrimaryAlias) - } - filter := &binlogdatapb.Filter{} - for _, table := range tables { - filter.Rules = append(filter.Rules, &binlogdatapb.Rule{ - Match: table, - }) - } - bls := &binlogdatapb.BinlogSource{ - Keyspace: fromKeyspace, - Shard: source.ShardName(), - Filter: filter, - } - cmd := binlogplayer.CreateVReplicationState("VSplitClone", bls, "", binlogplayer.BlpStopped, primary.DbName()) - qr, err := wr.TabletManagerClient().VReplicationExec(ctx, primary.Tablet, cmd) - if err != nil { - return vterrors.Wrapf(err, "VReplicationExec(%v, %s) failed", dest.PrimaryAlias, cmd) - } - if err := wr.SourceShardAdd(ctx, toKeyspace, dest.ShardName(), uint32(qr.InsertId), fromKeyspace, source.ShardName(), nil, tables); err != nil { - return vterrors.Wrapf(err, "SourceShardAdd(%s, %s) failed", dest.ShardName(), source.ShardName()) - } - // Start vreplication only if metadata was successfully created. - cmd = fmt.Sprintf("update _vt.vreplication set state='%s' where id=%d", binlogplayer.VReplicationInit, qr.InsertId) - if _, err = wr.TabletManagerClient().VReplicationExec(ctx, primary.Tablet, cmd); err != nil { - return vterrors.Wrapf(err, "VReplicationExec(%v, %s) failed", dest.PrimaryAlias, cmd) - } - return wr.refreshPrimaryTablets(ctx, []*topo.ShardInfo{dest}) -} - -// ShowResharding shows all resharding related metadata for the keyspace/shard. -func (wr *Wrangler) ShowResharding(ctx context.Context, keyspace, shard string) (err error) { - ki, err := wr.ts.GetKeyspace(ctx, keyspace) - if err != nil { - return err - } - if len(ki.ServedFroms) == 0 { - return wr.showHorizontalResharding(ctx, keyspace, shard) - } - return wr.showVerticalResharding(ctx, keyspace, shard) -} - -func (wr *Wrangler) showHorizontalResharding(ctx context.Context, keyspace, shard string) error { - osList, err := topotools.FindOverlappingShards(ctx, wr.ts, keyspace) - if err != nil { - return fmt.Errorf("FindOverlappingShards failed: %v", err) - } - os := topotools.OverlappingShardsForShard(osList, shard) - if os == nil { - wr.Logger().Printf("No resharding in progress\n") - return nil - } - - sourceShards, destinationShards, err := wr.findSourceDest(ctx, os) - if err != nil { - return err - } - wr.Logger().Printf("Horizontal Resharding for %v:\n", keyspace) - wr.Logger().Printf(" Sources:\n") - if err := wr.printShards(ctx, sourceShards); err != nil { - return err - } - wr.Logger().Printf(" Destinations:\n") - return wr.printShards(ctx, destinationShards) -} - func (wr *Wrangler) printShards(ctx context.Context, si []*topo.ShardInfo) error { for _, si := range si { wr.Logger().Printf(" Shard: %v\n", si.ShardName()) @@ -270,208 +120,6 @@ func (wr *Wrangler) printShards(ctx context.Context, si []*topo.ShardInfo) error return nil } -// CancelResharding cancels any resharding in progress on the specified keyspace/shard. -// This works for horizontal as well as vertical resharding. -func (wr *Wrangler) CancelResharding(ctx context.Context, keyspace, shard string) (err error) { - ctx, unlock, lockErr := wr.ts.LockKeyspace(ctx, keyspace, "CancelResharding") - if lockErr != nil { - return lockErr - } - defer unlock(&err) - - ki, err := wr.ts.GetKeyspace(ctx, keyspace) - if err != nil { - return err - } - if len(ki.ServedFroms) == 0 { - return wr.cancelHorizontalResharding(ctx, keyspace, shard) - } - return wr.cancelVerticalResharding(ctx, keyspace, shard) -} - -func (wr *Wrangler) cancelHorizontalResharding(ctx context.Context, keyspace, shard string) error { - wr.Logger().Infof("Finding the overlapping shards in keyspace %v", keyspace) - osList, err := topotools.FindOverlappingShards(ctx, wr.ts, keyspace) - if err != nil { - return fmt.Errorf("FindOverlappingShards failed: %v", err) - } - - // find our shard in there - os := topotools.OverlappingShardsForShard(osList, shard) - if os == nil { - return fmt.Errorf("shard %v is not involved in any overlapping shards", shard) - } - - _, destinationShards, err := wr.findSourceDest(ctx, os) - if err != nil { - return err - } - - // get srvKeyspaces in all cells to check if they are already serving this shard - srvKeyspaces, err := wr.ts.GetSrvKeyspaceAllCells(ctx, keyspace) - if err != nil { - return err - } - - for _, si := range destinationShards { - for _, srvKeyspace := range srvKeyspaces { - if topo.ShardIsServing(srvKeyspace, si.Shard) { - return fmt.Errorf("some served types have migrated for %v/%v, please undo them before canceling", keyspace, shard) - } - } - } - for i, si := range destinationShards { - ti, err := wr.ts.GetTablet(ctx, si.PrimaryAlias) - if err != nil { - return err - } - for _, sourceShard := range si.SourceShards { - if _, err := wr.tmc.VReplicationExec(ctx, ti.Tablet, binlogplayer.DeleteVReplication(sourceShard.Uid)); err != nil { - return err - } - } - updatedShard, err := wr.ts.UpdateShardFields(ctx, si.Keyspace(), si.ShardName(), func(si *topo.ShardInfo) error { - si.TabletControls = nil - si.SourceShards = nil - return nil - }) - if err != nil { - return err - } - - destinationShards[i] = updatedShard - - if _, _, err := topotools.RefreshTabletsByShard(ctx, wr.ts, wr.tmc, si, nil, wr.Logger()); err != nil { - return err - } - } - return nil -} - -// MigrateServedTypes is used during horizontal splits to migrate a -// served type from a list of shards to another. -func (wr *Wrangler) MigrateServedTypes(ctx context.Context, keyspace, shard string, cells []string, servedType topodatapb.TabletType, reverse, skipReFreshState bool, filteredReplicationWaitTime time.Duration, reverseReplication bool) (err error) { - // check input parameters - if servedType == topodatapb.TabletType_PRIMARY { - // we cannot migrate a primary back, since when primary migration - // is done, the source shards are dead - if reverse { - return fmt.Errorf("cannot migrate primary back to %v/%v", keyspace, shard) - } - // we cannot skip refresh state for a primary - if skipReFreshState { - return fmt.Errorf("cannot skip refresh state for primary migration on %v/%v", keyspace, shard) - } - if cells != nil { - return fmt.Errorf("cannot specify cells for primary migration on %v/%v", keyspace, shard) - } - } - - // lock the keyspace - ctx, unlock, lockErr := wr.ts.LockKeyspace(ctx, keyspace, fmt.Sprintf("MigrateServedTypes(%v)", servedType)) - if lockErr != nil { - return lockErr - } - defer unlock(&err) - - // find overlapping shards in this keyspace - wr.Logger().Infof("Finding the overlapping shards in keyspace %v", keyspace) - osList, err := topotools.FindOverlappingShards(ctx, wr.ts, keyspace) - if err != nil { - return fmt.Errorf("FindOverlappingShards failed: %v", err) - } - - // find our shard in there - os := topotools.OverlappingShardsForShard(osList, shard) - if os == nil { - return fmt.Errorf("shard %v is not involved in any overlapping shards", shard) - } - - sourceShards, destinationShards, err := wr.findSourceDest(ctx, os) - if err != nil { - return err - } - - // execute the migration - if servedType == topodatapb.TabletType_PRIMARY { - if err = wr.masterMigrateServedType(ctx, keyspace, sourceShards, destinationShards, filteredReplicationWaitTime, reverseReplication); err != nil { - return err - } - } else { - if err = wr.replicaMigrateServedType(ctx, keyspace, sourceShards, destinationShards, cells, servedType, reverse); err != nil { - return err - } - } - - // Primary migrate performs its own refresh. - // Otherwise, honor skipRefreshState if requested. - if servedType == topodatapb.TabletType_PRIMARY || skipReFreshState { - return nil - } - - // refresh - // TODO(b/26388813): Integrate vtctl WaitForDrain here instead of just sleeping. - // Anything that's not a replica will use the RDONLY sleep time. - // Primary Migrate performs its own refresh but we will refresh all non primary - // tablets after each migration - waitForDrainSleep := *waitForDrainSleepRdonly - if servedType == topodatapb.TabletType_REPLICA { - waitForDrainSleep = *waitForDrainSleepReplica - } - wr.Logger().Infof("WaitForDrain: Sleeping for %.0f seconds before shutting down query service on old tablets...", waitForDrainSleep.Seconds()) - time.Sleep(waitForDrainSleep) - wr.Logger().Infof("WaitForDrain: Sleeping finished. Shutting down queryservice on old tablets now.") - - rec := concurrency.AllErrorRecorder{} - refreshShards := sourceShards - if reverse { - // For a backwards migration, we should refresh (disable) destination shards instead. - refreshShards = destinationShards - } - for _, si := range refreshShards { - _, _, err := topotools.RefreshTabletsByShard(ctx, wr.ts, wr.tmc, si, cells, wr.Logger()) - rec.RecordError(err) - } - return rec.Error() -} - -// findSourceDest derives the source and destination from the overlapping shards. -// Whichever side has SourceShards is a destination. -func (wr *Wrangler) findSourceDest(ctx context.Context, os *topotools.OverlappingShards) (sourceShards, destinationShards []*topo.ShardInfo, err error) { - // It's possible that both source and destination have source shards because of reversible replication. - // If so, the Frozen flag in the tablet control record dictates the direction. - // So, check that first. - for _, left := range os.Left { - tc := left.GetTabletControl(topodatapb.TabletType_PRIMARY) - if tc == nil { - continue - } - if tc.Frozen { - return os.Left, os.Right, nil - } - } - for _, right := range os.Right { - tc := right.GetTabletControl(topodatapb.TabletType_PRIMARY) - if tc == nil { - continue - } - if tc.Frozen { - return os.Right, os.Left, nil - } - } - for _, left := range os.Left { - if len(left.SourceShards) != 0 { - return os.Right, os.Left, nil - } - } - for _, right := range os.Right { - if len(right.SourceShards) != 0 { - return os.Left, os.Right, nil - } - } - return nil, nil, fmt.Errorf("neither Shard '%v' nor Shard '%v' have a 'SourceShards' entry. Did you successfully run vtworker SplitClone before? Or did you already migrate the MASTER type?", os.Left[0].ShardName(), os.Right[0].ShardName()) -} - func (wr *Wrangler) getPrimaryPositions(ctx context.Context, shards []*topo.ShardInfo) (map[*topo.ShardInfo]string, error) { mu := sync.Mutex{} result := make(map[*topo.ShardInfo]string) @@ -575,310 +223,6 @@ func (wr *Wrangler) refreshPrimaryTablets(ctx context.Context, shards []*topo.Sh return rec.Error() } -// replicaMigrateServedType operates with the keyspace locked -func (wr *Wrangler) replicaMigrateServedType(ctx context.Context, keyspace string, sourceShards, destinationShards []*topo.ShardInfo, cells []string, servedType topodatapb.TabletType, reverse bool) (err error) { - ev := &events.MigrateServedTypes{ - KeyspaceName: keyspace, - SourceShards: sourceShards, - DestinationShards: destinationShards, - ServedType: servedType, - Reverse: reverse, - } - event.DispatchUpdate(ev, "start") - defer func() { - if err != nil { - event.DispatchUpdate(ev, "failed: "+err.Error()) - } - }() - - fromShards, toShards := sourceShards, destinationShards - if reverse { - fromShards, toShards = toShards, fromShards - } - - // Check and update all source shard records. - // Enable query service if needed - event.DispatchUpdate(ev, "updating shards to migrate from") - if err = wr.updateShardRecords(ctx, keyspace, fromShards, cells, servedType, true /* isFrom */, false /* clearSourceShards */); err != nil { - return err - } - - // Do the same for destination shards - event.DispatchUpdate(ev, "updating shards to migrate to") - if err = wr.updateShardRecords(ctx, keyspace, toShards, cells, servedType, false, false); err != nil { - return err - } - - // Now update serving keyspace - - if err = wr.ts.MigrateServedType(ctx, keyspace, toShards, fromShards, servedType, cells); err != nil { - return err - } - - event.DispatchUpdate(ev, "finished") - return nil -} - -// masterMigrateServedType operates with the keyspace locked -func (wr *Wrangler) masterMigrateServedType(ctx context.Context, keyspace string, sourceShards, destinationShards []*topo.ShardInfo, filteredReplicationWaitTime time.Duration, reverseReplication bool) (err error) { - // Ensure other served types have migrated. - srvKeyspaces, err := wr.ts.GetSrvKeyspaceAllCells(ctx, keyspace) - if err != nil { - return err - } - - si := sourceShards[0] - for _, srvKeyspace := range srvKeyspaces { - var shardServedTypes []string - for _, partition := range srvKeyspace.GetPartitions() { - if partition.GetServedType() != topodatapb.TabletType_PRIMARY { - for _, shardReference := range partition.GetShardReferences() { - if key.KeyRangeEqual(shardReference.GetKeyRange(), si.GetKeyRange()) { - shardServedTypes = append(shardServedTypes, partition.GetServedType().String()) - } - } - } - } - if len(shardServedTypes) > 0 { - return fmt.Errorf("cannot migrate MASTER away from %v/%v until everything else is migrated. Make sure that the following types are migrated first: %v", si.Keyspace(), si.ShardName(), strings.Join(shardServedTypes, ", ")) - } - } - - ev := &events.MigrateServedTypes{ - KeyspaceName: keyspace, - SourceShards: sourceShards, - DestinationShards: destinationShards, - ServedType: topodatapb.TabletType_PRIMARY, - } - event.DispatchUpdate(ev, "start") - defer func() { - if err != nil { - event.DispatchUpdate(ev, "failed: "+err.Error()) - } - }() - - // Phase 1 - // - check topology service can successfully refresh both source and target primary - // - switch the source shards to read-only by disabling query service - // - gather all replication points - // - wait for filtered replication to catch up - // - mark source shards as frozen - event.DispatchUpdate(ev, "disabling query service on all source primary tablets") - // making sure the refreshPrimaryTablets on both source and target are working before turning off query service on source - if err := wr.refreshPrimaryTablets(ctx, sourceShards); err != nil { - wr.cancelPrimaryMigrateServedTypes(ctx, keyspace, sourceShards) - return err - } - if err := wr.refreshPrimaryTablets(ctx, destinationShards); err != nil { - wr.cancelPrimaryMigrateServedTypes(ctx, keyspace, sourceShards) - return err - } - - if err := wr.updateShardRecords(ctx, keyspace, sourceShards, nil, topodatapb.TabletType_PRIMARY, true, false); err != nil { - wr.cancelPrimaryMigrateServedTypes(ctx, keyspace, sourceShards) - return err - } - if err := wr.refreshPrimaryTablets(ctx, sourceShards); err != nil { - wr.cancelPrimaryMigrateServedTypes(ctx, keyspace, sourceShards) - return err - } - - event.DispatchUpdate(ev, "getting positions of source primary tablets") - primaryPositions, err := wr.getPrimaryPositions(ctx, sourceShards) - if err != nil { - wr.cancelPrimaryMigrateServedTypes(ctx, keyspace, sourceShards) - return err - } - - event.DispatchUpdate(ev, "waiting for destination primary tablets to catch up") - if err := wr.waitForFilteredReplication(ctx, primaryPositions, destinationShards, filteredReplicationWaitTime); err != nil { - wr.cancelPrimaryMigrateServedTypes(ctx, keyspace, sourceShards) - return err - } - - // We've reached the point of no return. Freeze the tablet control records in the source primary tablets. - if err := wr.updateFrozenFlag(ctx, sourceShards, true); err != nil { - wr.cancelPrimaryMigrateServedTypes(ctx, keyspace, sourceShards) - return err - } - - // Phase 2 - // Always setup reverse replication. We'll start it later if reverseReplication was specified. - // This will allow someone to reverse the replication later if they change their mind. - if err := wr.setupReverseReplication(ctx, sourceShards, destinationShards); err != nil { - // It's safe to unfreeze if reverse replication setup fails. - wr.cancelPrimaryMigrateServedTypes(ctx, keyspace, sourceShards) - unfreezeErr := wr.updateFrozenFlag(ctx, sourceShards, false) - if unfreezeErr != nil { - wr.Logger().Errorf("Problem recovering for failed reverse replication: %v", unfreezeErr) - } - - return err - } - - // Destination shards need different handling than what updateShardRecords does. - event.DispatchUpdate(ev, "updating destination shards") - - // Enable query service - err = wr.ts.UpdateDisableQueryService(ctx, keyspace, destinationShards, topodatapb.TabletType_PRIMARY, nil, false) - if err != nil { - return err - } - - for i, si := range destinationShards { - ti, err := wr.ts.GetTablet(ctx, si.PrimaryAlias) - if err != nil { - return err - } - // Stop VReplication streams. - for _, sourceShard := range si.SourceShards { - if _, err := wr.tmc.VReplicationExec(ctx, ti.Tablet, binlogplayer.DeleteVReplication(sourceShard.Uid)); err != nil { - return err - } - } - // Similar to updateShardRecords, but we also remove SourceShards. - destinationShards[i], err = wr.ts.UpdateShardFields(ctx, si.Keyspace(), si.ShardName(), func(si *topo.ShardInfo) error { - si.SourceShards = nil - si.IsPrimaryServing = true - return nil - }) - if err != nil { - return err - } - } - - event.DispatchUpdate(ev, "setting destination primary tablets read-write") - if err := wr.refreshPrimaryTablets(ctx, destinationShards); err != nil { - return err - } - - // Update srvKeyspace now - if err = wr.ts.MigrateServedType(ctx, keyspace, destinationShards, sourceShards, topodatapb.TabletType_PRIMARY, nil); err != nil { - return err - } - - // Make sure that from now on source shards have IsPrimaryServing set to false - for _, si := range sourceShards { - _, err := wr.ts.UpdateShardFields(ctx, si.Keyspace(), si.ShardName(), func(si *topo.ShardInfo) error { - si.IsPrimaryServing = false - return nil - }) - if err != nil { - return err - } - } - - if reverseReplication { - if err := wr.startReverseReplication(ctx, sourceShards); err != nil { - return err - } - // We also have to remove the frozen flag as final step. - if err := wr.updateFrozenFlag(ctx, sourceShards, false); err != nil { - return err - } - } - - for _, si := range destinationShards { - if _, _, err := topotools.RefreshTabletsByShard(ctx, wr.ts, wr.tmc, si, nil, wr.Logger()); err != nil { - return err - } - } - - event.DispatchUpdate(ev, "finished") - return nil -} - -func (wr *Wrangler) cancelPrimaryMigrateServedTypes(ctx context.Context, keyspace string, sourceShards []*topo.ShardInfo) { - wr.Logger().Infof("source shards cancelPrimaryMigrateServedTypes: %v", sourceShards) - if err := wr.updateShardRecords(ctx, keyspace, sourceShards, nil, topodatapb.TabletType_PRIMARY, false, true); err != nil { - wr.Logger().Errorf2(err, "failed to re-enable source primary tablets") - return - } - if err := wr.refreshPrimaryTablets(ctx, sourceShards); err != nil { - wr.Logger().Errorf2(err, "failed to refresh source primary tablets") - } -} - -func (wr *Wrangler) setupReverseReplication(ctx context.Context, sourceShards, destinationShards []*topo.ShardInfo) error { - // Retrieve primary positions of all destinations. - primaryPositions := make([]string, len(destinationShards)) - for i, dest := range destinationShards { - ti, err := wr.ts.GetTablet(ctx, dest.PrimaryAlias) - if err != nil { - return err - } - - wr.Logger().Infof("Gathering primary position for %v", topoproto.TabletAliasString(dest.PrimaryAlias)) - primaryPositions[i], err = wr.tmc.PrimaryPosition(ctx, ti.Tablet) - if err != nil { - return err - } - } - - // Create reverse replication for each source. - for i, sourceShard := range sourceShards { - ti, err := wr.ts.GetTablet(ctx, sourceShard.PrimaryAlias) - if err != nil { - return err - } - dbName := ti.DbName() - if len(sourceShard.SourceShards) != 0 { - continue - } - // Handle the case where the source is "unsharded". - kr := sourceShard.KeyRange - if kr == nil { - kr = &topodatapb.KeyRange{} - } - // Create replications streams first using the retrieved primary positions. - uids := make([]uint32, len(destinationShards)) - for j, dest := range destinationShards { - bls := &binlogdatapb.BinlogSource{ - Keyspace: dest.Keyspace(), - Shard: dest.ShardName(), - KeyRange: kr, - } - qr, err := wr.VReplicationExec(ctx, sourceShard.PrimaryAlias, binlogplayer.CreateVReplicationState("ReversedResharding", bls, primaryPositions[j], binlogplayer.BlpStopped, dbName)) - if err != nil { - return err - } - uids[j] = uint32(qr.InsertId) - wr.Logger().Infof("Created reverse replication for tablet %v/%v: %v, db: %v, pos: %v, uid: %v", sourceShard.Keyspace(), sourceShard.ShardName(), bls, dbName, primaryPositions[j], uids[j]) - } - // Source shards have to be atomically added to ensure idempotence. - // If this fails, there's no harm because the unstarted vreplication streams will just be abandoned. - sourceShards[i], err = wr.ts.UpdateShardFields(ctx, sourceShard.Keyspace(), sourceShard.ShardName(), func(si *topo.ShardInfo) error { - for j, dest := range destinationShards { - si.SourceShards = append(si.SourceShards, &topodatapb.Shard_SourceShard{ - Uid: uids[j], - Keyspace: dest.Keyspace(), - Shard: dest.ShardName(), - KeyRange: dest.KeyRange, - }) - } - return nil - }) - if err != nil { - wr.Logger().Errorf("Unstarted vreplication streams for %v/%v need to be deleted: %v", sourceShard.Keyspace(), sourceShard.ShardName(), uids) - return fmt.Errorf("failed to setup reverse replication: %v, unstarted vreplication streams for %v/%v need to be deleted: %v", err, sourceShard.Keyspace(), sourceShard.ShardName(), uids) - } - } - return nil -} - -func (wr *Wrangler) startReverseReplication(ctx context.Context, sourceShards []*topo.ShardInfo) error { - for _, sourceShard := range sourceShards { - for _, dest := range sourceShard.SourceShards { - wr.Logger().Infof("Starting reverse replication for tablet %v/%v, uid: %v", sourceShard.Keyspace(), sourceShard.ShardName(), dest.Uid) - _, err := wr.VReplicationExec(ctx, sourceShard.PrimaryAlias, binlogplayer.StartVReplication(dest.Uid)) - if err != nil { - return err - } - } - } - return nil -} - // updateShardRecords updates the shard records based on 'from' or 'to' direction. func (wr *Wrangler) updateShardRecords(ctx context.Context, keyspace string, shards []*topo.ShardInfo, cells []string, servedType topodatapb.TabletType, isFrom bool, clearSourceShards bool) (err error) { return topotools.UpdateShardRecords(ctx, wr.ts, wr.tmc, keyspace, shards, cells, servedType, isFrom, clearSourceShards, wr.Logger()) @@ -911,395 +255,6 @@ func (wr *Wrangler) updateFrozenFlag(ctx context.Context, shards []*topo.ShardIn return nil } -// WaitForDrain blocks until the selected tablets (cells/keyspace/shard/tablet_type) -// have reported a QPS rate of 0.0. -// NOTE: This is just an observation of one point in time and no guarantee that -// the tablet was actually drained. At later times, a QPS rate > 0.0 could still -// be observed. -func (wr *Wrangler) WaitForDrain(ctx context.Context, cells []string, keyspace, shard string, servedType topodatapb.TabletType, - retryDelay, healthCheckTopologyRefresh, healthcheckRetryDelay, healthCheckTimeout, initialWait time.Duration) error { - var err error - if len(cells) == 0 { - // Retrieve list of cells for the shard from the topology. - cells, err = wr.ts.GetCellInfoNames(ctx) - if err != nil { - return fmt.Errorf("failed to retrieve list of all cells. GetCellInfoNames() failed: %v", err) - } - } - - // Check all cells in parallel. - wg := sync.WaitGroup{} - rec := concurrency.AllErrorRecorder{} - for _, cell := range cells { - wg.Add(1) - go func(cell string) { - defer wg.Done() - rec.RecordError(wr.waitForDrainInCell(ctx, cell, keyspace, shard, servedType, - retryDelay, healthCheckTopologyRefresh, healthcheckRetryDelay, healthCheckTimeout, initialWait)) - }(cell) - } - wg.Wait() - - return rec.Error() -} - -func (wr *Wrangler) waitForDrainInCell(ctx context.Context, cell, keyspace, shard string, servedType topodatapb.TabletType, - retryDelay, healthCheckTopologyRefresh, healthcheckRetryDelay, healthCheckTimeout, initialWait time.Duration) error { - - // Create the healthheck module, with a cache. - hc := discovery.NewLegacyHealthCheck(healthcheckRetryDelay, healthCheckTimeout) - defer hc.Close() - tsc := discovery.NewLegacyTabletStatsCache(hc, wr.TopoServer(), cell) - - // Create a tablet watcher. - watcher := discovery.NewLegacyShardReplicationWatcher(ctx, wr.TopoServer(), hc, cell, keyspace, shard, healthCheckTopologyRefresh, discovery.DefaultTopoReadConcurrency) - defer watcher.Stop() - - // Wait for at least one tablet. - if err := tsc.WaitForTablets(ctx, keyspace, shard, servedType); err != nil { - return fmt.Errorf("%v: error waiting for initial %v tablets for %v/%v: %v", cell, servedType, keyspace, shard, err) - } - - wr.Logger().Infof("%v: Waiting for %.1f seconds to make sure that the discovery module retrieves healthcheck information from all tablets.", - cell, initialWait.Seconds()) - // Wait at least for -initial_wait to elapse to make sure that we - // see all healthy tablets. Otherwise, we might miss some tablets. - // Note the default value for the parameter is set to the same - // default as healthcheck timeout, and it's safe to wait not - // longer for this because we would only miss slow tablets and - // vtgate would not serve from such tablets anyway. - time.Sleep(initialWait) - - // Now check the QPS rate of all tablets until the timeout expires. - startTime := time.Now() - for { - // map key: tablet uid - drainedHealthyTablets := make(map[uint32]*discovery.LegacyTabletStats) - notDrainedHealtyTablets := make(map[uint32]*discovery.LegacyTabletStats) - - healthyTablets := tsc.GetHealthyTabletStats(keyspace, shard, servedType) - for _, ts := range healthyTablets { - if ts.Stats.Qps == 0.0 { - drainedHealthyTablets[ts.Tablet.Alias.Uid] = &ts - } else { - notDrainedHealtyTablets[ts.Tablet.Alias.Uid] = &ts - } - } - - if len(drainedHealthyTablets) == len(healthyTablets) { - wr.Logger().Infof("%v: All %d healthy tablets were drained after %.1f seconds (not counting %.1f seconds for the initial wait).", - cell, len(healthyTablets), time.Since(startTime).Seconds(), healthCheckTimeout.Seconds()) - break - } - - // Continue waiting, sleep in between. - deadlineString := "" - if d, ok := ctx.Deadline(); ok { - deadlineString = fmt.Sprintf(" up to %.1f more seconds", time.Until(d).Seconds()) - } - wr.Logger().Infof("%v: Waiting%v for all healthy tablets to be drained (%d/%d done).", - cell, deadlineString, len(drainedHealthyTablets), len(healthyTablets)) - - timer := time.NewTimer(retryDelay) - select { - case <-ctx.Done(): - timer.Stop() - - var l []string - for _, ts := range notDrainedHealtyTablets { - l = append(l, formatTabletStats(ts)) - } - return fmt.Errorf("%v: WaitForDrain failed for %v tablets in %v/%v. Only %d/%d tablets were drained. err: %v List of tablets which were not drained: %v", - cell, servedType, keyspace, shard, len(drainedHealthyTablets), len(healthyTablets), ctx.Err(), strings.Join(l, ";")) - case <-timer.C: - } - } - - return nil -} - -func formatTabletStats(ts *discovery.LegacyTabletStats) string { - webURL := "unknown http port" - if webPort, ok := ts.Tablet.PortMap["vt"]; ok { - webURL = fmt.Sprintf("http://%v:%d/", ts.Tablet.Hostname, webPort) - } - return fmt.Sprintf("%v: %v stats: %v", topoproto.TabletAliasString(ts.Tablet.Alias), webURL, ts.Stats) -} - -func (wr *Wrangler) showVerticalResharding(ctx context.Context, keyspace, shard string) error { - ki, err := wr.ts.GetKeyspace(ctx, keyspace) - if err != nil { - return err - } - destinationShard, err := wr.ts.GetShard(ctx, keyspace, shard) - if err != nil { - return err - } - if len(destinationShard.SourceShards) != 1 || len(destinationShard.SourceShards[0].Tables) == 0 { - wr.Logger().Printf("No resharding in progress\n") - return nil - } - sourceShard, err := wr.ts.GetShard(ctx, destinationShard.SourceShards[0].Keyspace, destinationShard.SourceShards[0].Shard) - if err != nil { - return err - } - wr.Logger().Printf("Vertical Resharding:\n") - wr.Logger().Printf(" Served From: %v\n", ki.ServedFroms) - wr.Logger().Printf(" Source:\n") - if err := wr.printShards(ctx, []*topo.ShardInfo{sourceShard}); err != nil { - return err - } - wr.Logger().Printf(" Destination:\n") - return wr.printShards(ctx, []*topo.ShardInfo{destinationShard}) -} - -func (wr *Wrangler) cancelVerticalResharding(ctx context.Context, keyspace, shard string) error { - wr.Logger().Infof("Cancel vertical resharding in keyspace %v", keyspace) - destinationShard, err := wr.ts.GetShard(ctx, keyspace, shard) - if err != nil { - return err - } - if len(destinationShard.SourceShards) != 1 || len(destinationShard.SourceShards[0].Tables) == 0 { - return fmt.Errorf("destination shard %v/%v is not a vertical split target", keyspace, shard) - } - sourceShard, err := wr.ts.GetShard(ctx, destinationShard.SourceShards[0].Keyspace, destinationShard.SourceShards[0].Shard) - if err != nil { - return err - } - if len(sourceShard.TabletControls) != 0 { - return fmt.Errorf("some served types have migrated for %v/%v, please undo them before canceling", keyspace, shard) - } - destinationPrimaryTabletInfo, err := wr.ts.GetTablet(ctx, destinationShard.PrimaryAlias) - if err != nil { - return err - } - if _, err := wr.tmc.VReplicationExec(ctx, destinationPrimaryTabletInfo.Tablet, binlogplayer.DeleteVReplication(destinationShard.SourceShards[0].Uid)); err != nil { - return err - } - if _, err = wr.ts.UpdateShardFields(ctx, destinationShard.Keyspace(), destinationShard.ShardName(), func(si *topo.ShardInfo) error { - si.SourceShards = nil - return nil - }); err != nil { - return err - } - // set destination primary back to serving - return wr.refreshPrimaryTablets(ctx, []*topo.ShardInfo{destinationShard}) -} - -// MigrateServedFrom is used during vertical splits to migrate a -// served type from a keyspace to another. -func (wr *Wrangler) MigrateServedFrom(ctx context.Context, keyspace, shard string, servedType topodatapb.TabletType, cells []string, reverse bool, filteredReplicationWaitTime time.Duration) (err error) { - // read the destination keyspace, check it - ki, err := wr.ts.GetKeyspace(ctx, keyspace) - if err != nil { - return err - } - if len(ki.ServedFroms) == 0 { - return fmt.Errorf("destination keyspace %v is not a vertical split target", keyspace) - } - - // read the destination shard, check it - si, err := wr.ts.GetShard(ctx, keyspace, shard) - if err != nil { - return err - } - if len(si.SourceShards) != 1 || len(si.SourceShards[0].Tables) == 0 { - return fmt.Errorf("destination shard %v/%v is not a vertical split target", keyspace, shard) - } - - // check the migration is valid before locking (will also be checked - // after locking to be sure) - sourceKeyspace := si.SourceShards[0].Keyspace - if err := ki.CheckServedFromMigration(servedType, cells, sourceKeyspace, !reverse); err != nil { - return err - } - - // lock the keyspaces, source first. - ctx, unlock, lockErr := wr.ts.LockKeyspace(ctx, sourceKeyspace, fmt.Sprintf("MigrateServedFrom(%v)", servedType)) - if lockErr != nil { - return lockErr - } - defer unlock(&err) - ctx, unlock, lockErr = wr.ts.LockKeyspace(ctx, keyspace, fmt.Sprintf("MigrateServedFrom(%v)", servedType)) - if lockErr != nil { - return lockErr - } - defer unlock(&err) - - // execute the migration - err = wr.migrateServedFromLocked(ctx, ki, si, servedType, cells, reverse, filteredReplicationWaitTime) - - // rebuild the keyspace serving graph if there was no error - if err == nil { - err = topotools.RebuildKeyspaceLocked(ctx, wr.logger, wr.ts, keyspace, cells, false) - } - - return err -} - -func (wr *Wrangler) migrateServedFromLocked(ctx context.Context, ki *topo.KeyspaceInfo, destinationShard *topo.ShardInfo, servedType topodatapb.TabletType, cells []string, reverse bool, filteredReplicationWaitTime time.Duration) (err error) { - - // re-read and update keyspace info record - ki, err = wr.ts.GetKeyspace(ctx, ki.KeyspaceName()) - if err != nil { - return err - } - if reverse { - ki.UpdateServedFromMap(servedType, cells, destinationShard.SourceShards[0].Keyspace, false, nil) - } else { - destinationShardcells, err := wr.ts.GetShardServingCells(ctx, destinationShard) - if err != nil { - return err - } - ki.UpdateServedFromMap(servedType, cells, destinationShard.SourceShards[0].Keyspace, true, destinationShardcells) - } - - // re-read and check the destination shard - destinationShard, err = wr.ts.GetShard(ctx, destinationShard.Keyspace(), destinationShard.ShardName()) - if err != nil { - return err - } - if len(destinationShard.SourceShards) != 1 { - return fmt.Errorf("destination shard %v/%v is not a vertical split target", destinationShard.Keyspace(), destinationShard.ShardName()) - } - tables := destinationShard.SourceShards[0].Tables - - // read the source shard, we'll need its primary, and we'll need to - // update the list of denied tables. - var sourceShard *topo.ShardInfo - sourceShard, err = wr.ts.GetShard(ctx, destinationShard.SourceShards[0].Keyspace, destinationShard.SourceShards[0].Shard) - if err != nil { - return err - } - - ev := &events.MigrateServedFrom{ - KeyspaceName: ki.KeyspaceName(), - SourceShard: *sourceShard, - DestinationShard: *destinationShard, - ServedType: servedType, - Reverse: reverse, - } - event.DispatchUpdate(ev, "start") - defer func() { - if err != nil { - event.DispatchUpdate(ev, "failed: "+err.Error()) - } - }() - - if servedType == topodatapb.TabletType_PRIMARY { - err = wr.masterMigrateServedFrom(ctx, ki, sourceShard, destinationShard, tables, ev, filteredReplicationWaitTime) - } else { - err = wr.replicaMigrateServedFrom(ctx, ki, sourceShard, destinationShard, servedType, cells, reverse, tables, ev) - } - event.DispatchUpdate(ev, "finished") - return -} - -// replicaMigrateServedFrom handles the migration of (replica, rdonly). -func (wr *Wrangler) replicaMigrateServedFrom(ctx context.Context, ki *topo.KeyspaceInfo, sourceShard *topo.ShardInfo, destinationShard *topo.ShardInfo, servedType topodatapb.TabletType, cells []string, reverse bool, tables []string, ev *events.MigrateServedFrom) error { - // Save the destination keyspace (its ServedFrom has been changed) - event.DispatchUpdate(ev, "updating keyspace") - if err := wr.ts.UpdateKeyspace(ctx, ki); err != nil { - return err - } - - // Save the source shard (its denylist has changed) - event.DispatchUpdate(ev, "updating source shard") - if _, err := wr.ts.UpdateShardFields(ctx, sourceShard.Keyspace(), sourceShard.ShardName(), func(si *topo.ShardInfo) error { - return si.UpdateSourceDeniedTables(ctx, servedType, cells, reverse, tables) - }); err != nil { - return err - } - - // Now refresh the source servers so they reload the denylist - event.DispatchUpdate(ev, "refreshing sources tablets state so they update their denied tables") - _, _, err := topotools.RefreshTabletsByShard(ctx, wr.ts, wr.tmc, sourceShard, cells, wr.Logger()) - return err -} - -// masterMigrateServedFrom handles the primary migration. The ordering is -// a bit different than for rdonly / replica to guarantee a smooth transition. -// -// The order is as follows: -// - Add DeniedTables on the source shard map for primary -// - Refresh the source primary, so it stops writing on the tables -// - Get the source primary position, wait until destination primary reaches it -// - Clear SourceShard on the destination Shard -// - Refresh the destination primary, so its stops its filtered -// replication and starts accepting writes -func (wr *Wrangler) masterMigrateServedFrom(ctx context.Context, ki *topo.KeyspaceInfo, sourceShard *topo.ShardInfo, destinationShard *topo.ShardInfo, tables []string, ev *events.MigrateServedFrom, filteredReplicationWaitTime time.Duration) error { - // Read the data we need - ctx, cancel := context.WithTimeout(ctx, filteredReplicationWaitTime) - defer cancel() - sourcePrimaryTabletInfo, err := wr.ts.GetTablet(ctx, sourceShard.PrimaryAlias) - if err != nil { - return err - } - destinationPrimaryTabletInfo, err := wr.ts.GetTablet(ctx, destinationShard.PrimaryAlias) - if err != nil { - return err - } - - // Update source shard (tables will be added to the denylist) - event.DispatchUpdate(ev, "updating source shard") - if _, err := wr.ts.UpdateShardFields(ctx, sourceShard.Keyspace(), sourceShard.ShardName(), func(si *topo.ShardInfo) error { - return si.UpdateSourceDeniedTables(ctx, topodatapb.TabletType_PRIMARY, nil, false, tables) - }); err != nil { - return err - } - - // Now refresh the list of denied table list on the source primary - event.DispatchUpdate(ev, "refreshing source primary so it updates its denylist") - if err := wr.tmc.RefreshState(ctx, sourcePrimaryTabletInfo.Tablet); err != nil { - return err - } - - // get the position - event.DispatchUpdate(ev, "getting primary position") - primaryPosition, err := wr.tmc.PrimaryPosition(ctx, sourcePrimaryTabletInfo.Tablet) - if err != nil { - return err - } - - // wait for it - event.DispatchUpdate(ev, "waiting for destination primary to catch up to source primary") - uid := destinationShard.SourceShards[0].Uid - if err := wr.tmc.VReplicationWaitForPos(ctx, destinationPrimaryTabletInfo.Tablet, int(uid), primaryPosition); err != nil { - return err - } - - // Stop the VReplication stream. - event.DispatchUpdate(ev, "stopping vreplication") - if _, err := wr.tmc.VReplicationExec(ctx, destinationPrimaryTabletInfo.Tablet, binlogplayer.DeleteVReplication(uid)); err != nil { - return err - } - - // Update the destination keyspace (its ServedFrom has changed) - event.DispatchUpdate(ev, "updating keyspace") - if err = wr.ts.UpdateKeyspace(ctx, ki); err != nil { - return err - } - - // Update the destination shard (no more source shard) - event.DispatchUpdate(ev, "updating destination shard") - destinationShard, err = wr.ts.UpdateShardFields(ctx, destinationShard.Keyspace(), destinationShard.ShardName(), func(si *topo.ShardInfo) error { - if len(si.SourceShards) != 1 { - return fmt.Errorf("unexpected concurrent access for destination shard %v/%v SourceShards array", si.Keyspace(), si.ShardName()) - } - si.SourceShards = nil - return nil - }) - if err != nil { - return err - } - - // Tell the new shards primary tablets they can now be read-write. - // Invoking a remote action will also make the tablet stop filtered - // replication. - event.DispatchUpdate(ev, "setting destination shard primary tablets read-write") - return wr.refreshPrimaryTablets(ctx, []*topo.ShardInfo{destinationShard}) -} - func encodeString(in string) string { buf := bytes.NewBuffer(nil) sqltypes.NewVarChar(in).EncodeSQL(buf) diff --git a/go/vt/wrangler/schema.go b/go/vt/wrangler/schema.go index 1828515f716..dc2ba1f1c90 100644 --- a/go/vt/wrangler/schema.go +++ b/go/vt/wrangler/schema.go @@ -261,8 +261,7 @@ func (wr *Wrangler) CopySchemaShard(ctx context.Context, sourceTabletAlias *topo // where the database already existed on the destination, but with different // options e.g. a different character set. // In that case, MySQL would have skipped our CREATE DATABASE IF NOT EXISTS - // statement. We want to fail early in this case because vtworker SplitDiff - // fails in case of such an inconsistency as well. + // statement. if !skipVerify { diffs, err = schematools.CompareSchemas(ctx, wr.ts, wr.tmc, sourceTabletAlias, destShardInfo.PrimaryAlias, tables, excludeTables, includeViews) if err != nil { diff --git a/go/vt/wrangler/testlib/migrate_served_from_test.go b/go/vt/wrangler/testlib/migrate_served_from_test.go deleted file mode 100644 index 4d7456773a8..00000000000 --- a/go/vt/wrangler/testlib/migrate_served_from_test.go +++ /dev/null @@ -1,284 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package testlib - -import ( - "reflect" - "testing" - "time" - - "vitess.io/vitess/go/vt/discovery" - - "context" - - "vitess.io/vitess/go/mysql" - "vitess.io/vitess/go/sqltypes" - "vitess.io/vitess/go/vt/binlog/binlogplayer" - "vitess.io/vitess/go/vt/logutil" - "vitess.io/vitess/go/vt/topo/memorytopo" - "vitess.io/vitess/go/vt/vttablet/tabletmanager/vreplication" - "vitess.io/vitess/go/vt/vttablet/tmclient" - "vitess.io/vitess/go/vt/wrangler" - - topodatapb "vitess.io/vitess/go/vt/proto/topodata" -) - -func TestMigrateServedFrom(t *testing.T) { - delay := discovery.GetTabletPickerRetryDelay() - defer func() { - discovery.SetTabletPickerRetryDelay(delay) - }() - discovery.SetTabletPickerRetryDelay(5 * time.Millisecond) - - ctx := context.Background() - ts := memorytopo.NewServer("cell1", "cell2") - wr := wrangler.New(logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient()) - vp := NewVtctlPipe(t, ts) - defer vp.Close() - - // create the source keyspace tablets - sourcePrimary := NewFakeTablet(t, wr, "cell1", 10, topodatapb.TabletType_PRIMARY, nil, - TabletKeyspaceShard(t, "source", "0")) - sourceReplica := NewFakeTablet(t, wr, "cell1", 11, topodatapb.TabletType_REPLICA, nil, - TabletKeyspaceShard(t, "source", "0")) - sourceRdonly := NewFakeTablet(t, wr, "cell1", 12, topodatapb.TabletType_RDONLY, nil, - TabletKeyspaceShard(t, "source", "0")) - - // create the destination keyspace, served form source - // double check it has all entries in map - if err := vp.Run([]string{"CreateKeyspace", "-served_from", "master:source,replica:source,rdonly:source", "dest"}); err != nil { - t.Fatalf("CreateKeyspace(dest) failed: %v", err) - } - ki, err := ts.GetKeyspace(ctx, "dest") - if err != nil { - t.Fatalf("GetKeyspace failed: %v", err) - } - if len(ki.ServedFroms) != 3 { - t.Fatalf("bad initial dest ServedFroms: %+v", ki.ServedFroms) - } - - // create the destination keyspace tablets - destPrimary := NewFakeTablet(t, wr, "cell1", 20, topodatapb.TabletType_PRIMARY, nil, - TabletKeyspaceShard(t, "dest", "0")) - destReplica := NewFakeTablet(t, wr, "cell1", 21, topodatapb.TabletType_REPLICA, nil, - TabletKeyspaceShard(t, "dest", "0")) - destRdonly := NewFakeTablet(t, wr, "cell1", 22, topodatapb.TabletType_RDONLY, nil, - TabletKeyspaceShard(t, "dest", "0")) - - // sourceRdonly will see the refresh - sourceRdonly.StartActionLoop(t, wr) - defer sourceRdonly.StopActionLoop(t) - - // sourceReplica will see the refresh - sourceReplica.StartActionLoop(t, wr) - defer sourceReplica.StopActionLoop(t) - - // sourcePrimary will see the refresh, and has to respond to it - // also will be asked about its replication position. - sourcePrimary.FakeMysqlDaemon.CurrentPrimaryPosition = mysql.Position{ - GTIDSet: mysql.MariadbGTIDSet{ - 5: mysql.MariadbGTID{ - Domain: 5, - Server: 456, - Sequence: 892, - }, - }, - } - sourcePrimary.StartActionLoop(t, wr) - defer sourcePrimary.StopActionLoop(t) - - // destRdonly will see the refresh - destRdonly.StartActionLoop(t, wr) - defer destRdonly.StopActionLoop(t) - - // destReplica will see the refresh - destReplica.StartActionLoop(t, wr) - defer destReplica.StopActionLoop(t) - - destPrimary.StartActionLoop(t, wr) - defer destPrimary.StopActionLoop(t) - - // Override with a fake VREngine after TM is initialized in action loop. - dbClient := binlogplayer.NewMockDBClient(t) - dbClientFactory := func() binlogplayer.DBClient { return dbClient } - destPrimary.TM.VREngine = vreplication.NewTestEngine(ts, "", destPrimary.FakeMysqlDaemon, dbClientFactory, dbClientFactory, dbClient.DBName(), nil) - dbClient.ExpectRequest("select * from _vt.vreplication where db_name='db'", &sqltypes.Result{}, nil) - destPrimary.TM.VREngine.Open(context.Background()) - // select pos, state, message from _vt.vreplication - dbClient.ExpectRequest("select pos, state, message from _vt.vreplication where id=1", &sqltypes.Result{Rows: [][]sqltypes.Value{{ - sqltypes.NewVarBinary("MariaDB/5-456-892"), - sqltypes.NewVarBinary("Running"), - sqltypes.NewVarBinary(""), - }}}, nil) - expectDeleteVRepl(dbClient) - - // simulate the clone, by fixing the dest shard record - if err := vp.Run([]string{"SourceShardAdd", "--tables", "gone1,gone2", "dest/0", "1", "source/0"}); err != nil { - t.Fatalf("SourceShardAdd failed: %v", err) - } - - // migrate rdonly over in a cell - if err := vp.Run([]string{"MigrateServedFrom", "--cells", "cell1", "dest/0", "rdonly"}); err != nil { - t.Fatalf("MigrateServedFrom(rdonly) cell2 failed: %v", err) - } - - // check it's gone from keyspace - ki, err = ts.GetKeyspace(ctx, "dest") - if err != nil { - t.Fatalf("GetKeyspace failed: %v", err) - } - if len(ki.ServedFroms) != 2 || ki.GetServedFrom(topodatapb.TabletType_RDONLY) != nil { - t.Fatalf("bad initial dest ServedFroms: %v", ki.ServedFroms) - } - - // check the source shard has the right list of denied tables - si, err := ts.GetShard(ctx, "source", "0") - if err != nil { - t.Fatalf("GetShard failed: %v", err) - } - expected := []*topodatapb.Shard_TabletControl{ - { - TabletType: topodatapb.TabletType_RDONLY, - Cells: []string{"cell1"}, - DeniedTables: []string{"gone1", "gone2"}, - }, - } - if len(si.TabletControls) != 1 || !reflect.DeepEqual(si.TabletControls, expected) { - t.Fatalf("rdonly type doesn't have right list of denied tables. Expected: %v, got: %v", expected, si.TabletControls) - } - - // migrate rdonly reverse cell - if err := vp.Run([]string{"MigrateServedFrom", "--cells", "cell1", "--reverse", "dest/0", "rdonly"}); err != nil { - t.Fatalf("MigrateServedFrom(rdonly) cell2 failed: %v", err) - } - - // check it's gone from keyspace - ki, err = ts.GetKeyspace(ctx, "dest") - if err != nil { - t.Fatalf("GetKeyspace failed: %v", err) - } - if len(ki.ServedFroms) != 3 { - t.Fatalf("bad initial dest ServedFroms: %v", ki.ServedFroms) - } - - // check the source shard has the right list of denied tables - si, err = ts.GetShard(ctx, "source", "0") - if err != nil { - t.Fatalf("GetShard failed: %v", err) - } - - if len(si.TabletControls) != 0 { - t.Fatalf("rdonly type doesn't have right list of denied tables. Expected: nil, got: %v", si.TabletControls) - } - - // Now migrate rdonly over - if err := vp.Run([]string{"MigrateServedFrom", "dest/0", "rdonly"}); err != nil { - t.Fatalf("MigrateServedFrom(rdonly) failed: %v", err) - } - - // check it's gone from keyspace - ki, err = ts.GetKeyspace(ctx, "dest") - if err != nil { - t.Fatalf("GetKeyspace failed: %v", err) - } - if len(ki.ServedFroms) != 2 || ki.GetServedFrom(topodatapb.TabletType_RDONLY) != nil { - t.Fatalf("bad initial dest ServedFroms: %v", ki.ServedFroms) - } - - // check the source shard has the right list of denied tables - si, err = ts.GetShard(ctx, "source", "0") - if err != nil { - t.Fatalf("GetShard failed: %v", err) - } - expected = []*topodatapb.Shard_TabletControl{ - { - TabletType: topodatapb.TabletType_RDONLY, - DeniedTables: []string{"gone1", "gone2"}, - }, - } - if len(si.TabletControls) != 1 || !reflect.DeepEqual(si.TabletControls, expected) { - t.Fatalf("rdonly type doesn't have right list of denied tables. Expected: %v, got: %v", expected, si.TabletControls) - } - - // migrate replica over - if err := vp.Run([]string{"MigrateServedFrom", "dest/0", "replica"}); err != nil { - t.Fatalf("MigrateServedFrom(replica) failed: %v", err) - } - - // check it's gone from keyspace - ki, err = ts.GetKeyspace(ctx, "dest") - if err != nil { - t.Fatalf("GetKeyspace failed: %v", err) - } - if len(ki.ServedFroms) != 1 || ki.GetServedFrom(topodatapb.TabletType_REPLICA) != nil { - t.Fatalf("bad initial dest ServedFrom: %+v", ki.ServedFroms) - } - - // check the source shard has the right list of denied tables - si, err = ts.GetShard(ctx, "source", "0") - if err != nil { - t.Fatalf("GetShard failed: %v", err) - } - if len(si.TabletControls) != 2 || !reflect.DeepEqual(si.TabletControls, []*topodatapb.Shard_TabletControl{ - { - TabletType: topodatapb.TabletType_RDONLY, - DeniedTables: []string{"gone1", "gone2"}, - }, - { - TabletType: topodatapb.TabletType_REPLICA, - DeniedTables: []string{"gone1", "gone2"}, - }, - }) { - t.Fatalf("replica type doesn't have right list of denied tables") - } - - // migrate primary over - if err := vp.Run([]string{"MigrateServedFrom", "dest/0", "primary"}); err != nil { - t.Fatalf("MigrateServedFrom(master) failed: %v", err) - } - - // make sure ServedFromMap is empty - ki, err = ts.GetKeyspace(ctx, "dest") - if err != nil { - t.Fatalf("GetKeyspace failed: %v", err) - } - if len(ki.ServedFroms) > 0 { - t.Fatalf("dest keyspace still is ServedFrom: %+v", ki.ServedFroms) - } - - // check the source shard has the right list of denied tables - si, err = ts.GetShard(ctx, "source", "0") - if err != nil { - t.Fatalf("GetShard failed: %v", err) - } - if len(si.TabletControls) != 3 || !reflect.DeepEqual(si.TabletControls, []*topodatapb.Shard_TabletControl{ - { - TabletType: topodatapb.TabletType_RDONLY, - DeniedTables: []string{"gone1", "gone2"}, - }, - { - TabletType: topodatapb.TabletType_REPLICA, - DeniedTables: []string{"gone1", "gone2"}, - }, - { - TabletType: topodatapb.TabletType_PRIMARY, - DeniedTables: []string{"gone1", "gone2"}, - }, - }) { - t.Fatalf("master type doesn't have right list of denied tables") - } -} diff --git a/go/vt/wrangler/testlib/migrate_served_types_test.go b/go/vt/wrangler/testlib/migrate_served_types_test.go deleted file mode 100644 index 14ad107e5a3..00000000000 --- a/go/vt/wrangler/testlib/migrate_served_types_test.go +++ /dev/null @@ -1,584 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package testlib - -import ( - "flag" - "strings" - "testing" - "time" - - "vitess.io/vitess/go/vt/discovery" - - "context" - - "vitess.io/vitess/go/mysql" - "vitess.io/vitess/go/sqltypes" - "vitess.io/vitess/go/vt/binlog/binlogplayer" - "vitess.io/vitess/go/vt/logutil" - "vitess.io/vitess/go/vt/topo" - "vitess.io/vitess/go/vt/topo/memorytopo" - "vitess.io/vitess/go/vt/topotools" - "vitess.io/vitess/go/vt/vttablet/tabletmanager/vreplication" - "vitess.io/vitess/go/vt/vttablet/tmclient" - "vitess.io/vitess/go/vt/wrangler" - - topodatapb "vitess.io/vitess/go/vt/proto/topodata" -) - -func checkShardServedTypes(t *testing.T, ts *topo.Server, shard string, expected int) { - ctx := context.Background() - si, err := ts.GetShard(ctx, "ks", shard) - if err != nil { - t.Fatalf("GetShard failed: %v", err) - } - - servedTypes, err := ts.GetShardServingTypes(ctx, si) - if err != nil { - t.Fatalf("GetShard failed: %v", err) - } - - if len(servedTypes) != expected { - t.Fatalf("shard %v has wrong served types: got: %v, expected: %v", shard, len(servedTypes), expected) - } -} - -func checkShardSourceShards(t *testing.T, ts *topo.Server, shard string, expected int) { - ctx := context.Background() - si, err := ts.GetShard(ctx, "ks", shard) - if err != nil { - t.Fatalf("GetShard failed: %v", err) - } - if len(si.SourceShards) != expected { - t.Fatalf("shard %v has wrong SourceShards: %#v", shard, si.SourceShards) - } -} - -func TestMigrateServedTypes(t *testing.T) { - // TODO(b/26388813): Remove the next two lines once vtctl WaitForDrain is integrated in the vtctl MigrateServed* commands. - flag.Set("wait_for_drain_sleep_rdonly", "0s") - flag.Set("wait_for_drain_sleep_replica", "0s") - - ts := memorytopo.NewServer("cell1", "cell2") - wr := wrangler.New(logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient()) - vp := NewVtctlPipe(t, ts) - defer vp.Close() - - // create keyspace - if err := ts.CreateKeyspace(context.Background(), "ks", &topodatapb.Keyspace{}); err != nil { - t.Fatalf("CreateKeyspace failed: %v", err) - } - - // create the source shard - sourcePrimary := NewFakeTablet(t, wr, "cell1", 10, topodatapb.TabletType_PRIMARY, nil, - TabletKeyspaceShard(t, "ks", "0")) - sourceReplica := NewFakeTablet(t, wr, "cell1", 11, topodatapb.TabletType_REPLICA, nil, - TabletKeyspaceShard(t, "ks", "0")) - sourceRdonly := NewFakeTablet(t, wr, "cell1", 12, topodatapb.TabletType_RDONLY, nil, - TabletKeyspaceShard(t, "ks", "0")) - - // create the first destination shard - dest1Primary := NewFakeTablet(t, wr, "cell1", 20, topodatapb.TabletType_PRIMARY, nil, - TabletKeyspaceShard(t, "ks", "-80")) - dest1Replica := NewFakeTablet(t, wr, "cell1", 21, topodatapb.TabletType_REPLICA, nil, - TabletKeyspaceShard(t, "ks", "-80")) - dest1Rdonly := NewFakeTablet(t, wr, "cell1", 22, topodatapb.TabletType_RDONLY, nil, - TabletKeyspaceShard(t, "ks", "-80")) - - // create the second destination shard - dest2Primary := NewFakeTablet(t, wr, "cell1", 30, topodatapb.TabletType_PRIMARY, nil, - TabletKeyspaceShard(t, "ks", "80-")) - dest2Replica := NewFakeTablet(t, wr, "cell1", 31, topodatapb.TabletType_REPLICA, nil, - TabletKeyspaceShard(t, "ks", "80-")) - dest2Rdonly := NewFakeTablet(t, wr, "cell1", 32, topodatapb.TabletType_RDONLY, nil, - TabletKeyspaceShard(t, "ks", "80-")) - - // Build keyspace graph - err := topotools.RebuildKeyspace(context.Background(), logutil.NewConsoleLogger(), ts, "ks", []string{"cell1"}, false) - if err != nil { - t.Fatalf("RebuildKeyspaceLocked failed: %v", err) - } - // double check the shards have the right served types - checkShardServedTypes(t, ts, "0", 3) - checkShardServedTypes(t, ts, "-80", 0) - checkShardServedTypes(t, ts, "80-", 0) - - // sourceRdonly will see the refresh - sourceRdonly.StartActionLoop(t, wr) - defer sourceRdonly.StopActionLoop(t) - - // sourceReplica will see the refresh - sourceReplica.StartActionLoop(t, wr) - defer sourceReplica.StopActionLoop(t) - - // sourcePrimary will see the refresh, and has to respond to it - // also will be asked about its replication position. - sourcePrimary.FakeMysqlDaemon.CurrentPrimaryPosition = mysql.Position{ - GTIDSet: mysql.MariadbGTIDSet{ - 5: mysql.MariadbGTID{ - Domain: 5, - Server: 456, - Sequence: 892, - }, - }, - } - sourcePrimary.StartActionLoop(t, wr) - defer sourcePrimary.StopActionLoop(t) - - // dest1Rdonly will see the refresh - dest1Rdonly.StartActionLoop(t, wr) - defer dest1Rdonly.StopActionLoop(t) - - // dest1Replica will see the refresh - dest1Replica.StartActionLoop(t, wr) - defer dest1Replica.StopActionLoop(t) - - dest1Primary.StartActionLoop(t, wr) - defer dest1Primary.StopActionLoop(t) - - // Override with a fake VREngine after TM is initialized in action loop. - dbClient1 := binlogplayer.NewMockDBClient(t) - dbClientFactory1 := func() binlogplayer.DBClient { return dbClient1 } - dest1Primary.TM.VREngine = vreplication.NewTestEngine(ts, "", dest1Primary.FakeMysqlDaemon, dbClientFactory1, dbClientFactory1, dbClient1.DBName(), nil) - // select * from _vt.vreplication during Open - dbClient1.ExpectRequest("select * from _vt.vreplication where db_name='db'", &sqltypes.Result{}, nil) - dest1Primary.TM.VREngine.Open(context.Background()) - // select pos, state, message from _vt.vreplication - dbClient1.ExpectRequest("select pos, state, message from _vt.vreplication where id=1", &sqltypes.Result{Rows: [][]sqltypes.Value{{ - sqltypes.NewVarBinary("MariaDB/5-456-892"), - sqltypes.NewVarBinary("Running"), - sqltypes.NewVarBinary(""), - }}}, nil) - expectDeleteVRepl(dbClient1) - - // dest2Rdonly will see the refresh - dest2Rdonly.StartActionLoop(t, wr) - defer dest2Rdonly.StopActionLoop(t) - - // dest2Replica will see the refresh - dest2Replica.StartActionLoop(t, wr) - defer dest2Replica.StopActionLoop(t) - - dest2Primary.StartActionLoop(t, wr) - defer dest2Primary.StopActionLoop(t) - - // Override with a fake VREngine after TM is initialized in action loop. - dbClient2 := binlogplayer.NewMockDBClient(t) - dbClientFactory2 := func() binlogplayer.DBClient { return dbClient2 } - dest2Primary.TM.VREngine = vreplication.NewTestEngine(ts, "", dest2Primary.FakeMysqlDaemon, dbClientFactory2, dbClientFactory2, dbClient2.DBName(), nil) - // select * from _vt.vreplication during Open - dbClient2.ExpectRequest("select * from _vt.vreplication where db_name='db'", &sqltypes.Result{}, nil) - dest2Primary.TM.VREngine.Open(context.Background()) - // select pos, state, message from _vt.vreplication - dbClient2.ExpectRequest("select pos, state, message from _vt.vreplication where id=1", &sqltypes.Result{Rows: [][]sqltypes.Value{{ - sqltypes.NewVarBinary("MariaDB/5-456-892"), - sqltypes.NewVarBinary("Running"), - sqltypes.NewVarBinary(""), - }}}, nil) - expectDeleteVRepl(dbClient2) - - // migrate will error if the overlapping shards have no "SourceShard" entry - // and we cannot decide which shard is the source or the destination. - if err := vp.Run([]string{"MigrateServedTypes", "ks/0", "rdonly"}); err == nil || !strings.Contains(err.Error(), "' have a 'SourceShards' entry. Did you successfully run vtworker SplitClone before? Or did you already migrate the MASTER type?") { - t.Fatalf("MigrateServedType(rdonly) should fail if no 'SourceShards' entry is present: %v", err) - } - - // simulate the clone, by fixing the dest shard record - checkShardSourceShards(t, ts, "-80", 0) - checkShardSourceShards(t, ts, "80-", 0) - if err := vp.Run([]string{"SourceShardAdd", "--key_range=-", "ks/-80", "1", "ks/0"}); err != nil { - t.Fatalf("SourceShardAdd failed: %v", err) - } - if err := vp.Run([]string{"SourceShardAdd", "--key_range=-", "ks/80-", "1", "ks/0"}); err != nil { - t.Fatalf("SourceShardAdd failed: %v", err) - } - checkShardSourceShards(t, ts, "-80", 1) - checkShardSourceShards(t, ts, "80-", 1) - - // migrate rdonly over - if err := vp.Run([]string{"MigrateServedTypes", "ks/0", "rdonly"}); err != nil { - t.Fatalf("MigrateServedType(rdonly) failed: %v", err) - } - - checkShardServedTypes(t, ts, "0", 2) - checkShardServedTypes(t, ts, "-80", 1) - checkShardServedTypes(t, ts, "80-", 1) - checkShardSourceShards(t, ts, "-80", 1) - checkShardSourceShards(t, ts, "80-", 1) - - // migrate replica over - if err := vp.Run([]string{"MigrateServedTypes", "ks/0", "replica"}); err != nil { - t.Fatalf("MigrateServedType(replica) failed: %v", err) - } - - checkShardServedTypes(t, ts, "0", 1) - checkShardServedTypes(t, ts, "-80", 2) - checkShardServedTypes(t, ts, "80-", 2) - checkShardSourceShards(t, ts, "-80", 1) - checkShardSourceShards(t, ts, "80-", 1) - - // migrate primary over - if err := vp.Run([]string{"MigrateServedTypes", "ks/0", "primary"}); err != nil { - t.Fatalf("MigrateServedType(master) failed: %v", err) - } - - checkShardServedTypes(t, ts, "0", 0) - checkShardServedTypes(t, ts, "-80", 3) - checkShardServedTypes(t, ts, "80-", 3) - checkShardSourceShards(t, ts, "-80", 0) - checkShardSourceShards(t, ts, "80-", 0) -} - -func TestMultiShardMigrateServedTypes(t *testing.T) { - delay := discovery.GetTabletPickerRetryDelay() - defer func() { - discovery.SetTabletPickerRetryDelay(delay) - }() - discovery.SetTabletPickerRetryDelay(5 * time.Millisecond) - - // TODO(b/26388813): Remove the next two lines once vtctl WaitForDrain is integrated in the vtctl MigrateServed* commands. - flag.Set("wait_for_drain_sleep_rdonly", "0s") - flag.Set("wait_for_drain_sleep_replica", "0s") - - ts := memorytopo.NewServer("cell1", "cell2") - wr := wrangler.New(logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient()) - vp := NewVtctlPipe(t, ts) - defer vp.Close() - - // create keyspace - if err := ts.CreateKeyspace(context.Background(), "ks", &topodatapb.Keyspace{}); err != nil { - t.Fatalf("CreateKeyspace failed: %v", err) - } - - // create the first source shard - source1Primary := NewFakeTablet(t, wr, "cell1", 20, topodatapb.TabletType_PRIMARY, nil, - TabletKeyspaceShard(t, "ks", "-80")) - source1Replica := NewFakeTablet(t, wr, "cell1", 21, topodatapb.TabletType_REPLICA, nil, - TabletKeyspaceShard(t, "ks", "-80")) - source1Rdonly := NewFakeTablet(t, wr, "cell1", 22, topodatapb.TabletType_RDONLY, nil, - TabletKeyspaceShard(t, "ks", "-80")) - - // create the second source shard - source2Primary := NewFakeTablet(t, wr, "cell1", 30, topodatapb.TabletType_PRIMARY, nil, - TabletKeyspaceShard(t, "ks", "80-")) - source2Replica := NewFakeTablet(t, wr, "cell1", 31, topodatapb.TabletType_REPLICA, nil, - TabletKeyspaceShard(t, "ks", "80-")) - source2Rdonly := NewFakeTablet(t, wr, "cell1", 32, topodatapb.TabletType_RDONLY, nil, - TabletKeyspaceShard(t, "ks", "80-")) - - dest1Primary := NewFakeTablet(t, wr, "cell1", 40, topodatapb.TabletType_PRIMARY, nil, - TabletKeyspaceShard(t, "ks", "-40")) - dest1Replica := NewFakeTablet(t, wr, "cell1", 41, topodatapb.TabletType_REPLICA, nil, - TabletKeyspaceShard(t, "ks", "-40")) - dest1Rdonly := NewFakeTablet(t, wr, "cell1", 42, topodatapb.TabletType_RDONLY, nil, - TabletKeyspaceShard(t, "ks", "-40")) - - // create the second source shard - dest2Primary := NewFakeTablet(t, wr, "cell1", 50, topodatapb.TabletType_PRIMARY, nil, - TabletKeyspaceShard(t, "ks", "40-80")) - dest2Replica := NewFakeTablet(t, wr, "cell1", 51, topodatapb.TabletType_REPLICA, nil, - TabletKeyspaceShard(t, "ks", "40-80")) - dest2Rdonly := NewFakeTablet(t, wr, "cell1", 52, topodatapb.TabletType_RDONLY, nil, - TabletKeyspaceShard(t, "ks", "40-80")) - - dest3Primary := NewFakeTablet(t, wr, "cell1", 60, topodatapb.TabletType_PRIMARY, nil, - TabletKeyspaceShard(t, "ks", "80-c0")) - dest3Replica := NewFakeTablet(t, wr, "cell1", 61, topodatapb.TabletType_REPLICA, nil, - TabletKeyspaceShard(t, "ks", "80-c0")) - dest3Rdonly := NewFakeTablet(t, wr, "cell1", 62, topodatapb.TabletType_RDONLY, nil, - TabletKeyspaceShard(t, "ks", "80-c0")) - - // create the second source shard - dest4Primary := NewFakeTablet(t, wr, "cell1", 70, topodatapb.TabletType_PRIMARY, nil, - TabletKeyspaceShard(t, "ks", "c0-")) - dest4Replica := NewFakeTablet(t, wr, "cell1", 71, topodatapb.TabletType_REPLICA, nil, - TabletKeyspaceShard(t, "ks", "c0-")) - dest4Rdonly := NewFakeTablet(t, wr, "cell1", 72, topodatapb.TabletType_RDONLY, nil, - TabletKeyspaceShard(t, "ks", "c0-")) - - // Build keyspace graph - err := topotools.RebuildKeyspace(context.Background(), logutil.NewConsoleLogger(), ts, "ks", []string{"cell1"}, false) - if err != nil { - t.Fatalf("RebuildKeyspaceLocked failed: %v", err) - } - // double check the shards have the right served types - checkShardServedTypes(t, ts, "-80", 3) - checkShardServedTypes(t, ts, "80-", 3) - checkShardServedTypes(t, ts, "-40", 0) - checkShardServedTypes(t, ts, "40-80", 0) - checkShardServedTypes(t, ts, "80-c0", 0) - checkShardServedTypes(t, ts, "c0-", 0) - - // source1Rdonly will see the refresh - source1Rdonly.StartActionLoop(t, wr) - defer source1Rdonly.StopActionLoop(t) - - // source1Replica will see the refresh - source1Replica.StartActionLoop(t, wr) - defer source1Replica.StopActionLoop(t) - - // source1Primary will see the refresh, and has to respond to it - // also will be asked about its replication position. - source1Primary.FakeMysqlDaemon.CurrentPrimaryPosition = mysql.Position{ - GTIDSet: mysql.MariadbGTIDSet{ - 5: mysql.MariadbGTID{ - Domain: 5, - Server: 456, - Sequence: 892, - }, - }, - } - source1Primary.StartActionLoop(t, wr) - defer source1Primary.StopActionLoop(t) - - // // dest1Rdonly will see the refresh - dest1Rdonly.StartActionLoop(t, wr) - defer dest1Rdonly.StopActionLoop(t) - - // // dest1Replica will see the refresh - dest1Replica.StartActionLoop(t, wr) - defer dest1Replica.StopActionLoop(t) - - dest1Primary.StartActionLoop(t, wr) - defer dest1Primary.StopActionLoop(t) - - // // dest2Rdonly will see the refresh - dest2Rdonly.StartActionLoop(t, wr) - defer dest2Rdonly.StopActionLoop(t) - - // // dest2Replica will see the refresh - dest2Replica.StartActionLoop(t, wr) - defer dest2Replica.StopActionLoop(t) - - dest2Primary.StartActionLoop(t, wr) - defer dest2Primary.StopActionLoop(t) - - // Now let's kick off the process for the second shard. - - // source2Rdonly will see the refresh - source2Rdonly.StartActionLoop(t, wr) - defer source2Rdonly.StopActionLoop(t) - - // source2Replica will see the refresh - source2Replica.StartActionLoop(t, wr) - defer source2Replica.StopActionLoop(t) - - // source2Primary will see the refresh, and has to respond to it - // also will be asked about its replication position. - source2Primary.FakeMysqlDaemon.CurrentPrimaryPosition = mysql.Position{ - GTIDSet: mysql.MariadbGTIDSet{ - 5: mysql.MariadbGTID{ - Domain: 5, - Server: 456, - Sequence: 892, - }, - }, - } - source2Primary.StartActionLoop(t, wr) - defer source2Primary.StopActionLoop(t) - - // dest3Rdonly will see the refresh - dest3Rdonly.StartActionLoop(t, wr) - defer dest3Rdonly.StopActionLoop(t) - - // dest3Replica will see the refresh - dest3Replica.StartActionLoop(t, wr) - defer dest3Replica.StopActionLoop(t) - - dest3Primary.StartActionLoop(t, wr) - defer dest3Primary.StopActionLoop(t) - - // dest4Rdonly will see the refresh - dest4Rdonly.StartActionLoop(t, wr) - defer dest4Rdonly.StopActionLoop(t) - - // dest4Replica will see the refresh - dest4Replica.StartActionLoop(t, wr) - defer dest4Replica.StopActionLoop(t) - - dest4Primary.StartActionLoop(t, wr) - defer dest4Primary.StopActionLoop(t) - - // Override with a fake VREngine after TM is initialized in action loop. - dbClient1 := binlogplayer.NewMockDBClient(t) - dbClientFactory1 := func() binlogplayer.DBClient { return dbClient1 } - dest1Primary.TM.VREngine = vreplication.NewTestEngine(ts, "", dest1Primary.FakeMysqlDaemon, dbClientFactory1, dbClientFactory1, "db", nil) - // select * from _vt.vreplication during Open - dbClient1.ExpectRequest("select * from _vt.vreplication where db_name='db'", &sqltypes.Result{}, nil) - dest1Primary.TM.VREngine.Open(context.Background()) - // select pos, state, message from _vt.vreplication - dbClient1.ExpectRequest("select pos, state, message from _vt.vreplication where id=1", &sqltypes.Result{Rows: [][]sqltypes.Value{{ - sqltypes.NewVarBinary("MariaDB/5-456-892"), - sqltypes.NewVarBinary("Running"), - sqltypes.NewVarBinary(""), - }}}, nil) - expectDeleteVRepl(dbClient1) - - // Override with a fake VREngine after TM is initialized in action loop. - dbClient2 := binlogplayer.NewMockDBClient(t) - dbClientFactory2 := func() binlogplayer.DBClient { return dbClient2 } - dest2Primary.TM.VREngine = vreplication.NewTestEngine(ts, "", dest2Primary.FakeMysqlDaemon, dbClientFactory2, dbClientFactory2, "db", nil) - // select * from _vt.vreplication during Open - dbClient2.ExpectRequest("select * from _vt.vreplication where db_name='db'", &sqltypes.Result{}, nil) - dest2Primary.TM.VREngine.Open(context.Background()) - - // select pos, state, message from _vt.vreplication - dbClient2.ExpectRequest("select pos, state, message from _vt.vreplication where id=1", &sqltypes.Result{Rows: [][]sqltypes.Value{{ - sqltypes.NewVarBinary("MariaDB/5-456-892"), - sqltypes.NewVarBinary("Running"), - sqltypes.NewVarBinary(""), - }}}, nil) - expectDeleteVRepl(dbClient2) - - // migrate will error if the overlapping shards have no "SourceShard" entry - // and we cannot decide which shard is the source or the destination. - if err := vp.Run([]string{"MigrateServedTypes", "ks/-80", "rdonly"}); err == nil || !strings.Contains(err.Error(), "' have a 'SourceShards' entry. Did you successfully run vtworker SplitClone before? Or did you already migrate the MASTER type?") { - t.Fatalf("MigrateServedType(rdonly) should fail if no 'SourceShards' entry is present: %v", err) - } - - // simulate the clone, by fixing the dest shard record - checkShardSourceShards(t, ts, "-40", 0) - checkShardSourceShards(t, ts, "40-80", 0) - if err := vp.Run([]string{"SourceShardAdd", "--key_range=-", "ks/-40", "1", "ks/-80"}); err != nil { - t.Fatalf("SourceShardAdd failed: %v", err) - } - if err := vp.Run([]string{"SourceShardAdd", "--key_range=-", "ks/40-80", "1", "ks/-80"}); err != nil { - t.Fatalf("SourceShardAdd failed: %v", err) - } - checkShardSourceShards(t, ts, "-40", 1) - checkShardSourceShards(t, ts, "40-80", 1) - - // migrate rdonly over - if err := vp.Run([]string{"MigrateServedTypes", "ks/-80", "rdonly"}); err != nil { - t.Fatalf("MigrateServedType(rdonly) failed: %v", err) - } - - checkShardServedTypes(t, ts, "-80", 2) - checkShardServedTypes(t, ts, "-40", 1) - checkShardServedTypes(t, ts, "40-80", 1) - checkShardSourceShards(t, ts, "-40", 1) - checkShardSourceShards(t, ts, "40-80", 1) - - // migrate replica over - if err := vp.Run([]string{"MigrateServedTypes", "ks/-80", "replica"}); err != nil { - t.Fatalf("MigrateServedType(replica) failed: %v", err) - } - - checkShardServedTypes(t, ts, "-80", 1) - checkShardServedTypes(t, ts, "-40", 2) - checkShardServedTypes(t, ts, "40-80", 2) - checkShardSourceShards(t, ts, "-40", 1) - checkShardSourceShards(t, ts, "40-80", 1) - - // migrate primary over - if err := vp.Run([]string{"MigrateServedTypes", "ks/-80", "primary"}); err != nil { - t.Fatalf("MigrateServedType(master) failed: %v", err) - } - - checkShardServedTypes(t, ts, "-80", 0) - checkShardServedTypes(t, ts, "-40", 3) - checkShardServedTypes(t, ts, "40-80", 3) - checkShardSourceShards(t, ts, "-40", 0) - checkShardSourceShards(t, ts, "40-80", 0) - - // Now migrate the second destination shard - - // Override with a fake VREngine after TM is initialized in action loop. - dbClient1 = binlogplayer.NewMockDBClient(t) - dbClientFactory1 = func() binlogplayer.DBClient { return dbClient1 } - dest3Primary.TM.VREngine = vreplication.NewTestEngine(ts, "", dest3Primary.FakeMysqlDaemon, dbClientFactory1, dbClientFactory1, "db", nil) - // select * from _vt.vreplication during Open - dbClient1.ExpectRequest("select * from _vt.vreplication where db_name='db'", &sqltypes.Result{}, nil) - dest3Primary.TM.VREngine.Open(context.Background()) - // select pos, state, message from _vt.vreplication - dbClient1.ExpectRequest("select pos, state, message from _vt.vreplication where id=1", &sqltypes.Result{Rows: [][]sqltypes.Value{{ - sqltypes.NewVarBinary("MariaDB/5-456-892"), - sqltypes.NewVarBinary("Running"), - sqltypes.NewVarBinary(""), - }}}, nil) - expectDeleteVRepl(dbClient1) - - // Override with a fake VREngine after TM is initialized in action loop. - dbClient2 = binlogplayer.NewMockDBClient(t) - dbClientFactory2 = func() binlogplayer.DBClient { return dbClient2 } - dest4Primary.TM.VREngine = vreplication.NewTestEngine(ts, "", dest4Primary.FakeMysqlDaemon, dbClientFactory2, dbClientFactory2, "db", nil) - // select * from _vt.vreplication during Open - dbClient2.ExpectRequest("select * from _vt.vreplication where db_name='db'", &sqltypes.Result{}, nil) - dest4Primary.TM.VREngine.Open(context.Background()) - - // select pos, state, message from _vt.vreplication - dbClient2.ExpectRequest("select pos, state, message from _vt.vreplication where id=1", &sqltypes.Result{Rows: [][]sqltypes.Value{{ - sqltypes.NewVarBinary("MariaDB/5-456-892"), - sqltypes.NewVarBinary("Running"), - sqltypes.NewVarBinary(""), - }}}, nil) - expectDeleteVRepl(dbClient2) - - // // simulate the clone, by fixing the dest shard record - checkShardSourceShards(t, ts, "80-c0", 0) - checkShardSourceShards(t, ts, "c0-", 0) - if err := vp.Run([]string{"SourceShardAdd", "--key_range=-", "ks/80-c0", "1", "ks/80-"}); err != nil { - t.Fatalf("SourceShardAdd failed: %v", err) - } - if err := vp.Run([]string{"SourceShardAdd", "--key_range=-", "ks/c0-", "1", "ks/80-"}); err != nil { - t.Fatalf("SourceShardAdd failed: %v", err) - } - checkShardSourceShards(t, ts, "80-c0", 1) - checkShardSourceShards(t, ts, "c0-", 1) - - // // migrate rdonly over - if err := vp.Run([]string{"MigrateServedTypes", "ks/80-", "rdonly"}); err != nil { - t.Fatalf("MigrateServedType(rdonly) failed: %v", err) - } - - checkShardServedTypes(t, ts, "80-", 2) - checkShardServedTypes(t, ts, "80-c0", 1) - checkShardServedTypes(t, ts, "c0-", 1) - checkShardSourceShards(t, ts, "80-c0", 1) - checkShardSourceShards(t, ts, "c0-", 1) - - // // migrate replica over - if err := vp.Run([]string{"MigrateServedTypes", "ks/80-", "replica"}); err != nil { - t.Fatalf("MigrateServedType(replica) failed: %v", err) - } - - checkShardServedTypes(t, ts, "80-", 1) - checkShardServedTypes(t, ts, "80-c0", 2) - checkShardServedTypes(t, ts, "c0-", 2) - checkShardSourceShards(t, ts, "80-c0", 1) - checkShardSourceShards(t, ts, "c0-", 1) - - // // migrate primary over - if err := vp.Run([]string{"MigrateServedTypes", "ks/80-", "primary"}); err != nil { - t.Fatalf("MigrateServedType(master) failed: %v", err) - } - - checkShardServedTypes(t, ts, "80-", 0) - checkShardServedTypes(t, ts, "80-c0", 3) - checkShardServedTypes(t, ts, "c0-", 3) - checkShardSourceShards(t, ts, "80-c0", 0) - checkShardSourceShards(t, ts, "c0-", 0) -} - -func expectDeleteVRepl(dbClient *binlogplayer.MockDBClient) { - dbClient.ExpectRequest("use _vt", &sqltypes.Result{}, nil) - dbClient.ExpectRequest("select id from _vt.vreplication where id = 1", &sqltypes.Result{Rows: [][]sqltypes.Value{{sqltypes.NewInt64(1)}}}, nil) - dbClient.ExpectRequest("begin", nil, nil) - dbClient.ExpectRequest("delete from _vt.vreplication where id in (1)", &sqltypes.Result{RowsAffected: 1}, nil) - dbClient.ExpectRequest("delete from _vt.copy_state where vrepl_id in (1)", nil, nil) - dbClient.ExpectRequest("commit", nil, nil) -} diff --git a/go/vt/wrangler/testlib/wait_for_drain_test.go b/go/vt/wrangler/testlib/wait_for_drain_test.go deleted file mode 100644 index e38df70090f..00000000000 --- a/go/vt/wrangler/testlib/wait_for_drain_test.go +++ /dev/null @@ -1,181 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package testlib - -import ( - "flag" - "io" - "strings" - "testing" - - "context" - - "vitess.io/vitess/go/vt/logutil" - "vitess.io/vitess/go/vt/topo/memorytopo" - "vitess.io/vitess/go/vt/vttablet/grpcqueryservice" - "vitess.io/vitess/go/vt/vttablet/queryservice/fakes" - "vitess.io/vitess/go/vt/vttablet/tmclient" - "vitess.io/vitess/go/vt/wrangler" - - logutilpb "vitess.io/vitess/go/vt/proto/logutil" - querypb "vitess.io/vitess/go/vt/proto/query" - topodatapb "vitess.io/vitess/go/vt/proto/topodata" -) - -type drainDirective int - -const ( - DrainNoCells drainDirective = 1 << iota - DrainCell1 - DrainCell2 -) - -func TestWaitForDrain(t *testing.T) { - testWaitForDrain(t, "both cells selected and drained", "" /* cells */, DrainCell1|DrainCell2, nil /* expectedErrors */) -} - -func TestWaitForDrain_SelectCell1(t *testing.T) { - testWaitForDrain(t, "cell1 selected and drained", "cell1", DrainCell1, nil /* expectedErrors */) -} - -func TestWaitForDrain_NoCellDrained(t *testing.T) { - testWaitForDrain(t, "both cells selected and none drained", "" /* cells */, DrainNoCells, []string{"cell1-0000000000", "cell2-0000000001"}) -} - -func TestWaitForDrain_SelectCell1ButCell2Drained(t *testing.T) { - testWaitForDrain(t, "cell1 selected and cell2 drained", "cell1", DrainCell2, []string{"cell1-0000000000"}) -} - -func testWaitForDrain(t *testing.T, desc, cells string, drain drainDirective, expectedErrors []string) { - const keyspace = "ks" - const shard = "-80" - - // This value needs to be bigger than the -initial_wait value below. - // Otherwise in this test, we close the StreamHealth RPC because of - // tablet inactivity at the same time as the end of the initial wait, - // and the test fails. - flag.Set("vtctl_healthcheck_timeout", "1s") - - ts := memorytopo.NewServer("cell1", "cell2") - wr := wrangler.New(logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient()) - vp := NewVtctlPipe(t, ts) - defer vp.Close() - - // Create keyspace. - if err := ts.CreateKeyspace(context.Background(), keyspace, &topodatapb.Keyspace{}); err != nil { - t.Fatalf("CreateKeyspace failed: %v", err) - } - - t1 := NewFakeTablet(t, wr, "cell1", 0, topodatapb.TabletType_REPLICA, nil, - TabletKeyspaceShard(t, keyspace, shard)) - t2 := NewFakeTablet(t, wr, "cell2", 1, topodatapb.TabletType_REPLICA, nil, - TabletKeyspaceShard(t, keyspace, shard)) - - target := &querypb.Target{ - Keyspace: keyspace, - Shard: shard, - TabletType: topodatapb.TabletType_REPLICA, - } - fqs1 := fakes.NewStreamHealthQueryService(target) - fqs2 := fakes.NewStreamHealthQueryService(target) - grpcqueryservice.Register(t1.RPCServer, fqs1) - grpcqueryservice.Register(t2.RPCServer, fqs2) - - // Start the action loop after having registered the extra services. - for _, ft := range []*FakeTablet{t1, t2} { - ft.StartActionLoop(t, wr) - defer ft.StopActionLoop(t) - } - - // Run vtctl WaitForDrain and react depending on its output. - timeout := "0.5s" - if len(expectedErrors) == 0 { - // Tests with a positive outcome should have a more generous timeout to - // avoid flakyness. - timeout = "30s" - } - stream, err := vp.RunAndStreamOutput( - []string{"WaitForDrain", - "-cells", cells, - "-retry_delay", "100ms", - "-timeout", timeout, - "-initial_wait", "100ms", - keyspace + "/" + shard, - topodatapb.TabletType_REPLICA.String()}) - if err != nil { - t.Fatalf("VtctlPipe.RunAndStreamOutput() failed: %v", err) - } - - // QPS = 1.0. Tablets are not drained yet. - fqs1.AddHealthResponseWithQPS(1.0) - fqs2.AddHealthResponseWithQPS(1.0) - - var le *logutilpb.Event - for { - le, err = stream.Recv() - if err != nil { - break - } - line := logutil.EventString(le) - t.Logf(line) - if strings.Contains(line, "for all healthy tablets to be drained") { - t.Log("Successfully waited for WaitForDrain to be blocked because tablets have a QPS rate > 0.0") - break - } else { - t.Log("waiting for WaitForDrain to see a QPS rate > 0.0") - } - } - - if drain&DrainCell1 != 0 { - fqs1.AddHealthResponseWithQPS(0.0) - } else { - fqs1.AddHealthResponseWithQPS(2.0) - } - if drain&DrainCell2 != 0 { - fqs2.AddHealthResponseWithQPS(0.0) - } else { - fqs2.AddHealthResponseWithQPS(2.0) - } - - // If a cell was drained, rate should go below <0.0 now. - // If not all selected cells were drained, this will end after "-timeout". - for { - le, err = stream.Recv() - if err == nil { - vp.t.Logf(logutil.EventString(le)) - } else { - break - } - } - - if len(expectedErrors) == 0 { - if err != io.EOF { - t.Fatalf("TestWaitForDrain: %v: no error expected but got: %v", desc, err) - } - // else: Success. - } else { - if err == nil || err == io.EOF { - t.Fatalf("TestWaitForDrain: %v: error expected but got none", desc) - } - for _, errString := range expectedErrors { - if !strings.Contains(err.Error(), errString) { - t.Fatalf("TestWaitForDrain: %v: error does not include expected string. got: %v want: %v", desc, err, errString) - } - } - // Success. - } -} diff --git a/proto/throttlerservice.proto b/proto/throttlerservice.proto index 7c717990c7f..91ea700a238 100644 --- a/proto/throttlerservice.proto +++ b/proto/throttlerservice.proto @@ -15,8 +15,7 @@ limitations under the License. */ // gRPC RPC interface for the internal resharding throttler (go/vt/throttler) -// which is used by the resharding clone process (vtworker) and filtered -// replication (vttablet). +// which is used by vreplication. syntax = "proto3"; option go_package = "vitess.io/vitess/go/vt/proto/throttlerservice"; diff --git a/proto/vtctlservice.proto b/proto/vtctlservice.proto index 0a76010aeba..49a38022278 100644 --- a/proto/vtctlservice.proto +++ b/proto/vtctlservice.proto @@ -199,11 +199,6 @@ service Vtctld { rpc RunHealthCheck(vtctldata.RunHealthCheckRequest) returns (vtctldata.RunHealthCheckResponse) {}; // SetKeyspaceDurabilityPolicy updates the DurabilityPolicy for a keyspace. rpc SetKeyspaceDurabilityPolicy(vtctldata.SetKeyspaceDurabilityPolicyRequest) returns (vtctldata.SetKeyspaceDurabilityPolicyResponse) {}; - // SetKeyspaceServedFrom changes the ServedFromMap manually, and is intended - // only for emergency fixes. This does not rebuild the serving graph. - // - // The ServedFromMap is automatically updated as a part of MigrateServedFrom. - rpc SetKeyspaceServedFrom(vtctldata.SetKeyspaceServedFromRequest) returns (vtctldata.SetKeyspaceServedFromResponse) {}; // SetShardIsPrimaryServing adds or removes a shard from serving. // // This is meant as an emergency function. It does not rebuild any serving diff --git a/proto/vtworkerdata.proto b/proto/vtworkerdata.proto deleted file mode 100644 index d94721d2b17..00000000000 --- a/proto/vtworkerdata.proto +++ /dev/null @@ -1,34 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Data structures for the vtworker RPC interface. - -syntax = "proto3"; -option go_package = "vitess.io/vitess/go/vt/proto/vtworkerdata"; - -package vtworkerdata; - -import "logutil.proto"; - -// ExecuteVtworkerCommandRequest is the payload for ExecuteVtworkerCommand. -message ExecuteVtworkerCommandRequest { - repeated string args = 1; -} - -// ExecuteVtworkerCommandResponse is streamed back by ExecuteVtworkerCommand. -message ExecuteVtworkerCommandResponse { - logutil.Event event = 1; -} diff --git a/proto/vtworkerservice.proto b/proto/vtworkerservice.proto deleted file mode 100644 index 7b0e87841e6..00000000000 --- a/proto/vtworkerservice.proto +++ /dev/null @@ -1,32 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// RPC interface for vtworker. -// The interface is very similar to the vtctld interface (see vtctlservice.proto). - -syntax = "proto3"; -option go_package = "vitess.io/vitess/go/vt/proto/vtworkerservice"; - -package vtworkerservice; - -import "vtworkerdata.proto"; - -// Vtworker contains the vtworker RPC calls. -service Vtworker { - // ExecuteVtworkerCommand allows to run a vtworker command by specifying the - // same arguments as on the command line. - rpc ExecuteVtworkerCommand (vtworkerdata.ExecuteVtworkerCommandRequest) returns (stream vtworkerdata.ExecuteVtworkerCommandResponse) {}; -} diff --git a/test/ci_workflow_gen.go b/test/ci_workflow_gen.go index 055da2eb6fa..fe9275a3001 100644 --- a/test/ci_workflow_gen.go +++ b/test/ci_workflow_gen.go @@ -73,14 +73,12 @@ var ( "13", "ers_prs_newfeatures_heavy", "15", - "shardedrecovery_stress_verticalsplit_heavy", "vtgate_general_heavy", "19", "xb_backup", "21", "22", - "worker_vault_heavy", - "24", + "mysql_server_vault", "26", "vstream_failover", "vstream_stoponreshard_true", @@ -119,8 +117,6 @@ var ( "vtgate_queries", "vtgate_schema_tracker", "xb_recovery", - "resharding", - "resharding_bytes", "mysql80", "vreplication_across_db_versions", "vreplication_multicell", @@ -144,7 +140,7 @@ var ( } clustersRequiringMakeTools = []string{ "18", - "24", + "mysql_server_vault", "vtgate_topo_consul", "tabletmanager_consul", } diff --git a/test/config.json b/test/config.json index d4595af4217..c2269440d24 100644 --- a/test/config.json +++ b/test/config.json @@ -80,17 +80,6 @@ "RetryMax": 1, "Tags": [] }, - "legacy_local_example": { - "File": "", - "Args": [], - "Command": [ - "test/legacy_local_example.sh" - ], - "Manual": false, - "Shard": "", - "RetryMax": 1, - "Tags": [] - }, "region_example": { "File": "", "Args": [], @@ -188,7 +177,7 @@ "Args": ["vitess.io/vitess/go/test/endtoend/mysqlserver"], "Command": [], "Manual": false, - "Shard": "24", + "Shard": "mysql_server_vault", "RetryMax": 1, "Tags": [] }, @@ -228,33 +217,6 @@ "RetryMax": 1, "Tags": [] }, - "initial_sharding": { - "File": "unused.go", - "Args": ["vitess.io/vitess/go/test/endtoend/sharding/initialsharding/v3"], - "Command": [], - "Manual": false, - "Shard": "13", - "RetryMax": 1, - "Tags": [] - }, - "initial_sharding_bytes": { - "File": "unused.go", - "Args": ["vitess.io/vitess/go/test/endtoend/sharding/initialsharding/bytes"], - "Command": [], - "Manual": false, - "Shard": "13", - "RetryMax": 1, - "Tags": [] - }, - "initial_sharding_multi": { - "File": "unused.go", - "Args": ["vitess.io/vitess/go/test/endtoend/sharding/initialsharding/multi"], - "Command": [], - "Manual": false, - "Shard": "", - "RetryMax": 3, - "Tags": [] - }, "keyspace": { "File": "unused.go", "Args": ["vitess.io/vitess/go/test/endtoend/keyspace"], @@ -266,28 +228,6 @@ "site_test" ] }, - "merge_sharding": { - "File": "unused.go", - "Args": ["vitess.io/vitess/go/test/endtoend/sharding/mergesharding/int"], - "Command": [], - "Manual": false, - "Shard": "22", - "RetryMax": 2, - "Tags": [ - "worker_test" - ] - }, - "merge_sharding_bytes": { - "File": "unused.go", - "Args": ["vitess.io/vitess/go/test/endtoend/sharding/mergesharding/string"], - "Command": [], - "Manual": false, - "Shard": "22", - "RetryMax": 1, - "Tags": [ - "worker_test" - ] - }, "mysqlctl": { "File": "unused.go", "Args": ["vitess.io/vitess/go/test/endtoend/mysqlctl"], @@ -467,28 +407,6 @@ "RetryMax": 1, "Tags": [""] }, - "resharding": { - "File": "unused.go", - "Args": ["vitess.io/vitess/go/test/endtoend/sharding/resharding/v3"], - "Command": [], - "Manual": false, - "Shard": "resharding", - "RetryMax": 1, - "Tags": [ - "worker_test" - ] - }, - "resharding_bytes": { - "File": "unused.go", - "Args": ["vitess.io/vitess/go/test/endtoend/sharding/resharding/string"], - "Command": [], - "Manual": false, - "Shard": "resharding_bytes", - "RetryMax": 2, - "Tags": [ - "worker_test" - ] - }, "sharded": { "File": "unused.go", "Args": ["vitess.io/vitess/go/test/endtoend/sharded"], @@ -498,24 +416,6 @@ "RetryMax": 1, "Tags": [] }, - "sharded_recovery": { - "File": "unused.go", - "Args": ["vitess.io/vitess/go/test/endtoend/recovery/shardedrecovery"], - "Command": [], - "Manual": false, - "Shard": "shardedrecovery_stress_verticalsplit_heavy", - "RetryMax": 2, - "Tags": [] - }, - "stress": { - "File": "unused.go", - "Args": ["vitess.io/vitess/go/test/endtoend/stress"], - "Command": [], - "Manual": false, - "Shard": "shardedrecovery_stress_verticalsplit_heavy", - "RetryMax": 1, - "Tags": [] - }, "tabletgateway_buffer_reparent": { "File": "unused.go", "Args": ["vitess.io/vitess/go/test/endtoend/tabletgateway/buffer/reparent"], @@ -631,17 +531,6 @@ "RetryMax": 1, "Tags": [] }, - "vertical_split": { - "File": "unused.go", - "Args": ["vitess.io/vitess/go/test/endtoend/sharding/verticalsplit"], - "Command": [], - "Manual": false, - "Shard": "shardedrecovery_stress_verticalsplit_heavy", - "RetryMax": 1, - "Tags": [ - "worker_test" - ] - }, "vtgate": { "File": "unused.go", "Args": ["vitess.io/vitess/go/test/endtoend/vtgate"], @@ -1074,17 +963,6 @@ "RetryMax": 1, "Tags": [] }, - "worker": { - "File": "worker_test.go", - "Args": ["vitess.io/vitess/go/test/endtoend/worker"], - "Command": [], - "Manual": false, - "Shard": "worker_vault_heavy", - "RetryMax": 3, - "Tags": [ - "worker_test" - ] - }, "vreplication_multicell": { "File": "unused.go", "Args": ["vitess.io/vitess/go/test/endtoend/vreplication", "-run", "MultiCell"], @@ -1234,7 +1112,7 @@ "Args": ["vitess.io/vitess/go/test/endtoend/vault"], "Command": [], "Manual": false, - "Shard": "worker_vault_heavy", + "Shard": "mysql_server_vault", "RetryMax": 1, "Tags": [] }, diff --git a/test/legacy_local_example.sh b/test/legacy_local_example.sh deleted file mode 100755 index b230307c997..00000000000 --- a/test/legacy_local_example.sh +++ /dev/null @@ -1,71 +0,0 @@ -#!/bin/bash - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# This test runs through the scripts in examples/legacy_local to make sure they work. -# This is currently a copy of examples/local which will be updated soon to use the -# new resharding workflows - -source build.env - -set -xe - -cd "$VTROOT/examples/legacy_local" - -unset VTROOT # ensure that the examples can run without VTROOT now. - -./101_initial_cluster.sh - -sleep 5 # Give vtgate time to really start. - -mysql -h 127.0.0.1 -P 15306 < ../common/insert_commerce_data.sql -mysql -h 127.0.0.1 -P 15306 --table < ../common/select_commerce_data.sql -./201_customer_keyspace.sh -./202_customer_tablets.sh -./203_vertical_split.sh -mysql -h 127.0.0.1 -P 15306 --table < ../common/select_customer0_data.sql - -./204_vertical_migrate_replicas.sh -./205_vertical_migrate_master.sh -# Expected to fail! -mysql -h 127.0.0.1 -P 15306 --table < ../common/select_commerce_data.sql || echo "DenyList working as expected" -./206_clean_commerce.sh -# Expected to fail! -mysql -h 127.0.0.1 -P 15306 --table < ../common/select_commerce_data.sql || echo "Tables missing as expected" - -./301_customer_sharded.sh -./302_new_shards.sh - -# Wait for the schema to be targetable before proceeding -# TODO: Eliminate this race in the examples' scripts -for shard in "customer/-80" "customer/80-"; do - while true; do - mysql -h 127.0.0.1 -P 15306 "$shard" -e 'show tables' && break || echo "waiting for shard: $shard!" - sleep 1 - done; -done; - -mysql -h 127.0.0.1 -P 15306 --table < ../common/select_customer-80_data.sql -mysql -h 127.0.0.1 -P 15306 --table < ../common/select_customer80-_data.sql - -./303_horizontal_split.sh - -./304_migrate_replicas.sh -./305_migrate_master.sh - -mysql -h 127.0.0.1 -P 15306 --table < ../common/select_customer-80_data.sql - -./401_teardown.sh - diff --git a/tools/coverage-go/vtworker_test.go b/tools/coverage-go/vtworker_test.go deleted file mode 100644 index 1a077e21b4a..00000000000 --- a/tools/coverage-go/vtworker_test.go +++ /dev/null @@ -1,22 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -package main - -import "testing" - -func TestVtworker(t *testing.T) { - main() -} diff --git a/tools/coverage-go/vtworkerclient_test.go b/tools/coverage-go/vtworkerclient_test.go deleted file mode 100644 index e1e6fa1ce0d..00000000000 --- a/tools/coverage-go/vtworkerclient_test.go +++ /dev/null @@ -1,22 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -package main - -import "testing" - -func TestVtworkerclient(t *testing.T) { - main() -} diff --git a/tools/make-release-packages.sh b/tools/make-release-packages.sh index 17fb0a73b28..36450530c9e 100755 --- a/tools/make-release-packages.sh +++ b/tools/make-release-packages.sh @@ -35,7 +35,7 @@ mkdir -p releases # Copy a subset of binaries from issue #5421 mkdir -p "${RELEASE_DIR}/bin" -for binary in vttestserver mysqlctl mysqlctld query_analyzer topo2topo vtaclcheck vtadmin vtbackup vtbench vtclient vtcombo vtctl vtctldclient vtctlclient vtctld vtexplain vtgate vttablet vtorc vtworker vtworkerclient zk zkctl zkctld; do +for binary in vttestserver mysqlctl mysqlctld query_analyzer topo2topo vtaclcheck vtadmin vtbackup vtbench vtclient vtcombo vtctl vtctldclient vtctlclient vtctld vtexplain vtgate vttablet vtorc zk zkctl zkctld; do cp "bin/$binary" "${RELEASE_DIR}/bin/" done; diff --git a/vitess-mixin/config.libsonnet b/vitess-mixin/config.libsonnet index 05c093f3265..8c8f69ca0ee 100644 --- a/vitess-mixin/config.libsonnet +++ b/vitess-mixin/config.libsonnet @@ -5,7 +5,6 @@ vtctldSelector: 'job="vitess-vtctld"', vtgateSelector: 'job="vitess-vtgate"', vttabletSelector: 'job="vitess-vttablet"', - vtworkerSelector: 'job="vitess-vtworker"', vtgateNodeSelector: 'job="node-exporter-vitess-vtgate"', mysqlSelector: 'job="mysql"', defaultTimeFrom: 'now-30m', diff --git a/vitess-mixin/dashboards/layouts/cluster_overview.libsonnet b/vitess-mixin/dashboards/layouts/cluster_overview.libsonnet index 8af0ab331f4..6db716c982c 100644 --- a/vitess-mixin/dashboards/layouts/cluster_overview.libsonnet +++ b/vitess-mixin/dashboards/layouts/cluster_overview.libsonnet @@ -29,7 +29,6 @@ local config = import '../../config.libsonnet'; singlestats.vtgateUp { gridPos: { h: 2, w: 2, x: 20, y: 3 } }, singlestats.vtctldUp { gridPos: { h: 2, w: 2, x: 20, y: 5 } }, singlestats.vttabletUp { gridPos: { h: 2, w: 2, x: 22, y: 3 } }, - singlestats.vtworkerUp { gridPos: { h: 2, w: 2, x: 22, y: 5 } }, helpers.vtgate.getPanel(config.vtgate.panels.vtgateRequests) { gridPos: { h: 6, w: 8, x: 0, y: 7 } }, helpers.vtgate.getPanel(config.vtgate.panels.vtgateErrorRate) { gridPos: { h: 6, w: 8, x: 8, y: 7 } }, diff --git a/vitess-mixin/dashboards/resources/grafonnet/panels.libsonnet b/vitess-mixin/dashboards/resources/grafonnet/panels.libsonnet index 2c31d1493fb..40c20a2cb10 100644 --- a/vitess-mixin/dashboards/resources/grafonnet/panels.libsonnet +++ b/vitess-mixin/dashboards/resources/grafonnet/panels.libsonnet @@ -6,7 +6,7 @@ local graphPanel = grafana.graphPanel; local prometheus = grafana.prometheus; // TODO: add description for each panel. -// TODO: create a _helper _config file for each group [vtctld,vtworker], +// TODO: create a _helper _config file for each group [vtctld], { // _ _ _ _ // __ _| |_ ___| |_| | __| | @@ -15,13 +15,6 @@ local prometheus = grafana.prometheus; // \_/ \__\___|\__|_|\__,_| // - // _ _ - // __ _| |___ _____ _ __| | _____ _ __ - // \ \ / / __\ \ /\ / / _ \| '__| |/ / _ \ '__| - // \ V /| |_ \ V V / (_) | | | < __/ | - // \_/ \__| \_/\_/ \___/|_| |_|\_\___|_| - // - // _ // _ __ ___ (_)___ ___ // | '_ ` _ \| / __|/ __| diff --git a/vitess-mixin/dashboards/resources/grafonnet/singlestats.libsonnet b/vitess-mixin/dashboards/resources/grafonnet/singlestats.libsonnet index 0a726b8842a..e3930e382d8 100644 --- a/vitess-mixin/dashboards/resources/grafonnet/singlestats.libsonnet +++ b/vitess-mixin/dashboards/resources/grafonnet/singlestats.libsonnet @@ -179,27 +179,6 @@ local prometheus = grafana.prometheus; ) ), - vtworkerUp:: - singlestat.new( - 'vtworker', - datasource='%(dataSource)s' % config._config, - valueFontSize='50%', - valueName='current', - ) - .addTarget( - prometheus.target( - ||| - sum( - up{ - %(vtworkerSelector)s - } - ) - ||| % config._config, - instant=true, - intervalFactor=1 - ) - ), - mysqlQPS:: singlestat.new( 'QPS - MySQL', diff --git a/vitess-mixin/e2e/cypress/integration/cluster_overview_spec.js b/vitess-mixin/e2e/cypress/integration/cluster_overview_spec.js index bd35ad97b57..5bb40a68c3b 100644 --- a/vitess-mixin/e2e/cypress/integration/cluster_overview_spec.js +++ b/vitess-mixin/e2e/cypress/integration/cluster_overview_spec.js @@ -41,7 +41,6 @@ describe('vitess-mixin: Cluster Overview Dashboard Test', function () { 'keyspace', 'shard', 'vtctld', - 'vtworker', 'Requests', 'Error rate', 'Duration 99th quantile', @@ -54,4 +53,4 @@ describe('vitess-mixin: Cluster Overview Dashboard Test', function () { ]) }) }) -}) \ No newline at end of file +}) diff --git a/vitess-mixin/e2e/docker-compose.vt.yml b/vitess-mixin/e2e/docker-compose.vt.yml index 62c33783100..a19fba574cf 100644 --- a/vitess-mixin/e2e/docker-compose.vt.yml +++ b/vitess-mixin/e2e/docker-compose.vt.yml @@ -394,19 +394,6 @@ services: - "3306" volumes: - .:/script - vtwork: - command: - - sh - - -c - - '/vt/bin/vtworker -topo_implementation consul -topo_global_server_address consul1:8500 - -topo_global_root vitess/global -cell test -logtostderr=true -service_map ''grpc-vtworker'' - -port 8080 -grpc_port 15999 -use_v3_resharding_mode=true ' - depends_on: - - vtctld - image: vitess/lite:${VITESS_TAG:-latest} - ports: - - "8080" - - "15999" prometheus: image: prom/prometheus:v2.21.0 ports: diff --git a/vitess-mixin/e2e/prometheus/prometheus.yml b/vitess-mixin/e2e/prometheus/prometheus.yml index e74acb8edbe..bd8fe98dfa7 100644 --- a/vitess-mixin/e2e/prometheus/prometheus.yml +++ b/vitess-mixin/e2e/prometheus/prometheus.yml @@ -26,11 +26,6 @@ scrape_configs: - 'vttablet202:8080' - 'vttablet301:8080' - 'vttablet302:8080' - - job_name: vitess-vtworker - metrics_path: /metrics - static_configs: - - targets: - - 'vtwork:8080' # Mock mysql exporter for vttablet - job_name: mysql metrics_path: /metrics @@ -63,4 +58,4 @@ scrape_configs: - source_labels: [ instance ] target_label: instance action: replace - replacement: vttablet101:8080 \ No newline at end of file + replacement: vttablet101:8080 diff --git a/vitess-mixin/e2e/vtcompose/docker-compose.test.yml b/vitess-mixin/e2e/vtcompose/docker-compose.test.yml index be97cfb4260..1ce97ca4848 100644 --- a/vitess-mixin/e2e/vtcompose/docker-compose.test.yml +++ b/vitess-mixin/e2e/vtcompose/docker-compose.test.yml @@ -301,18 +301,4 @@ services: - "3306" volumes: - .:/script - vtwork: - command: - - sh - - -c - - '$$VTROOT/bin/vtworker -topo_implementation consul -topo_global_server_address - consul1:8500 -topo_global_root vitess/global -cell test -logtostderr=true -service_map - ''grpc-vtworker'' -port 8080 -grpc_port 15999 -use_v3_resharding_mode=true -pid_file - $$VTDATAROOT/tmp/vtwork.pid ' - depends_on: - - vtctld - image: vitess/base - ports: - - 15100:8080 - - "15999" version: "2.1" diff --git a/vitess-mixin/e2e/vtcompose/vtcompose.go b/vitess-mixin/e2e/vtcompose/vtcompose.go index df1c26b8b32..5f1398b3a57 100644 --- a/vitess-mixin/e2e/vtcompose/vtcompose.go +++ b/vitess-mixin/e2e/vtcompose/vtcompose.go @@ -478,7 +478,6 @@ func applyDefaultDockerPatches( dockerComposeFile = applyInMemoryPatch(dockerComposeFile, generateVtctld(opts)) dockerComposeFile = applyInMemoryPatch(dockerComposeFile, generateVtgate(opts)) - dockerComposeFile = applyInMemoryPatch(dockerComposeFile, generateVtwork(opts)) dockerComposeFile = applyInMemoryPatch(dockerComposeFile, generateVreplication(dbInfo, opts)) dockerComposeFile = applyInMemoryPatch(dockerComposeFile, generateVtorc(dbInfo, opts)) return dockerComposeFile @@ -721,29 +720,6 @@ func generateVtgate(opts vtOptions) string { `, opts.webPort, opts.gRpcPort, opts.mySqlPort, opts.topologyFlags, opts.cell) } -func generateVtwork(opts vtOptions) string { - return fmt.Sprintf(` -- op: add - path: /services/vtwork - value: - image: vitess/lite:${VITESS_TAG:-latest} - ports: - - "%[1]d" - - "%[2]d" - command: ["sh", "-c", "/vt/bin/vtworker \ - %[3]s \ - -cell %[4]s \ - -logtostderr=true \ - -service_map 'grpc-vtworker' \ - -port %[1]d \ - -grpc_port %[2]d \ - -use_v3_resharding_mode=true \ - "] - depends_on: - - vtctld -`, opts.webPort, opts.gRpcPort, opts.topologyFlags, opts.cell) -} - func generateVtorc(dbInfo externalDbInfo, opts vtOptions) string { externalDb := "0" if dbInfo.dbName != "" {