Repository: vitessio/vitess
Pull request #11306 (2022-09-21 13:37:28)

feat: rewrite column names in HAVING
## Description
When trying to figure out the offsets for some HAVING and ORDER BY expression, it's much easier for the planner if we have the real aggregation function and not the column alias introduced. This PR rewrites these queries, so later phases of the planner don't have to take this possibility into account.

Example:

```sql
select id, sum(foo) as sumOfFoo from tbl group by id having sumOfFoo > 10
```

will get rewritten to:

```sql
select id, sum(foo) as sumOfFoo from tbl group by id having sum(foo) > 10
```


vitess-bot[bot]: ### Review Checklist
            
Hello reviewers! :wave: Please follow this checklist when reviewing this Pull Request.

#### General
- [ ] Ensure that the Pull Request has a descriptive title.
- [ ] If this is a change that users need to know about, please apply the `release notes (needs details)` label so that merging is blocked unless the summary release notes document is included.
- [ ] If a new flag is being introduced, review whether it is really needed. The flag names should be clear and intuitive (as far as possible), and the flag's help should be descriptive. Additionally, flag names should use dashes (`-`) as word separators rather than underscores (`_`).
- [ ] If a workflow is added or modified, each items in `Jobs` should be named in order to mark it as `required`. If the workflow should be required, the GitHub Admin should be notified.

#### Bug fixes
- [ ] There should be at least one unit or end-to-end test.
- [ ] The Pull Request description should either include a link to an issue that describes the bug OR an actual description of the bug and how to reproduce, along with a description of the fix.

#### Non-trivial changes
- [ ] There should be some code comments as to why things are implemented the way they are.

#### New/Existing features
- [ ] Should be documented, either by modifying the existing documentation or creating new documentation.
- [ ] New features should have a link to a feature request issue or an RFC that documents the use cases, corner cases and test cases.

#### Backward compatibility            
- [ ] Protobuf changes should be wire-compatible.
- [ ] Changes to `_vt` tables and RPCs need to be backward compatible.
- [ ] `vtctl` command output order should be stable and `awk`-able.


Patch:
diff --git a/.github/workflows/cluster_endtoend_12.yml b/.github/workflows/cluster_endtoend_12.yml
index be5e30822c..7f794c840c 100644
--- a/.github/workflows/cluster_endtoend_12.yml
+++ b/.github/workflows/cluster_endtoend_12.yml
@@ -6,71 +6,113 @@ concurrency:
   group: format('{0}-{1}', ${{ github.ref }}, 'Cluster (12)')
   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 (12)
-    runs-on: self-hosted
+    runs-on: ubuntu-18.04
 
     steps:
-      - name: Check if workflow needs to be skipped
-        id: skip-workflow
-        run: |
-          skip='false'
-          if [[ "${{github.event.pull_request}}" ==  "" ]] && [[ "${{github.ref}}" != "refs/heads/main" ]] && [[ ! "${{github.ref}}" =~ ^refs/heads/release-[0-9]+\.[0-9]$ ]] && [[ ! "${{github.ref}}" =~ "refs/tags/.*" ]]; then
-            skip='true'
-          fi
-          echo Skip ${skip}
-          echo "::set-output name=skip-workflow::${skip}"
-
-      - name: Check out code
-        if: steps.skip-workflow.outputs.skip-workflow == 'false'
-        uses: actions/checkout@v2
-
-      - name: Check for changes in relevant files
-        if: steps.skip-workflow.outputs.skip-workflow == 'false'
-        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/**'
-              - '.github/docker/**'
-              - 'bootstrap.sh'
-              - '.github/workflows/cluster_endtoend_12.yml'
-
-      - name: Build Docker Image
-        if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
-        run: docker build -f ./.github/docker/cluster_test_12/Dockerfile -t cluster_test_12:$GITHUB_SHA  .
-
-      - name: Run test
-        if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
-        timeout-minutes: 30
-        run: docker run --name "cluster_test_12_$GITHUB_SHA" cluster_test_12:$GITHUB_SHA /bin/bash -c 'source build.env && go run test.go -keep-data=true -docker=false -print-log -follow -shard 12 -- -- --keep-data=true'
-
-      - name: Print Volume Used
-        if: always() && steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
-        run: |
-          docker inspect -f '{{ (index .Mounts 0).Name }}' cluster_test_12_$GITHUB_SHA
-
-      - name: Cleanup Docker Volume
-        if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
-        run: |
-          docker rm -v cluster_test_12_$GITHUB_SHA
-
-      - name: Cleanup Docker Container
-        if: always() && steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
-        run: |
-          docker rm -f cluster_test_12_$GITHUB_SHA
-
-      - name: Cleanup Docker Image
-        if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
-        run: |
-          docker image rm cluster_test_12:$GITHUB_SHA
+    - name: Check if workflow needs to be skipped
+      id: skip-workflow
+      run: |
+        skip='false'
+        if [[ "${{github.event.pull_request}}" ==  "" ]] && [[ "${{github.ref}}" != "refs/heads/main" ]] && [[ ! "${{github.ref}}" =~ ^refs/heads/release-[0-9]+\.[0-9]$ ]] && [[ ! "${{github.ref}}" =~ "refs/tags/.*" ]]; then
+          skip='true'
+        fi
+        echo Skip ${skip}
+        echo "::set-output name=skip-workflow::${skip}"
+
+    - name: Check out code
+      if: steps.skip-workflow.outputs.skip-workflow == 'false'
+      uses: actions/checkout@v2
+
+    - name: Check for changes in relevant files
+      if: steps.skip-workflow.outputs.skip-workflow == 'false'
+      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/cluster_endtoend_12.yml'
+
+    - name: Set up Go
+      if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
+      uses: actions/setup-go@v2
+      with:
+        go-version: 1.18.5
+
+    - name: Set up python
+      if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
+      uses: actions/setup-python@v2
+
+    - name: Tune the OS
+      if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
+      run: |
+        echo '1024 65535' | sudo tee -a /proc/sys/net/ipv4/ip_local_port_range
+        # Increase the asynchronous non-blocking I/O. More information at https://dev.mysql.com/doc/refman/5.7/en/innodb-parameters.html#sysvar_innodb_use_native_aio
+        echo "fs.aio-max-nr = 1048576" | sudo tee -a /etc/sysctl.conf
+        sudo sysctl -p /etc/sysctl.conf
+
+    - name: Get dependencies
+      if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
+      run: |
+        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/vitessio/go-junit-report@HEAD
+
+    - name: Setup launchable dependencies
+      if: steps.skip-workflow.outputs.skip-workflow == 'false' && 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.skip-workflow.outputs.skip-workflow == 'false' && 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 12 | tee -a output.txt | go-junit-report -set-exit-code > report.xml
+
+    - name: Print test output and Record test result in launchable
+      if: steps.skip-workflow.outputs.skip-workflow == 'false' && 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_18.yml b/.github/workflows/cluster_endtoend_18.yml
index 77c2d9b797..08086ca7c0 100644
--- a/.github/workflows/cluster_endtoend_18.yml
+++ b/.github/workflows/cluster_endtoend_18.yml
@@ -6,71 +6,118 @@ concurrency:
   group: format('{0}-{1}', ${{ github.ref }}, 'Cluster (18)')
   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 (18)
-    runs-on: self-hosted
+    runs-on: ubuntu-18.04
 
     steps:
-      - name: Check if workflow needs to be skipped
-        id: skip-workflow
-        run: |
-          skip='false'
-          if [[ "${{github.event.pull_request}}" ==  "" ]] && [[ "${{github.ref}}" != "refs/heads/main" ]] && [[ ! "${{github.ref}}" =~ ^refs/heads/release-[0-9]+\.[0-9]$ ]] && [[ ! "${{github.ref}}" =~ "refs/tags/.*" ]]; then
-            skip='true'
-          fi
-          echo Skip ${skip}
-          echo "::set-output name=skip-workflow::${skip}"
-
-      - name: Check out code
-        if: steps.skip-workflow.outputs.skip-workflow == 'false'
-        uses: actions/checkout@v2
-
-      - name: Check for changes in relevant files
-        if: steps.skip-workflow.outputs.skip-workflow == 'false'
-        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/**'
-              - '.github/docker/**'
-              - 'bootstrap.sh'
-              - '.github/workflows/cluster_endtoend_18.yml'
-
-      - name: Build Docker Image
-        if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
-        run: docker build -f ./.github/docker/cluster_test_18/Dockerfile -t cluster_test_18:$GITHUB_SHA  .
-
-      - name: Run test
-        if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
-        timeout-minutes: 30
-        run: docker run --name "cluster_test_18_$GITHUB_SHA" cluster_test_18:$GITHUB_SHA /bin/bash -c 'source build.env && go run test.go -keep-data=true -docker=false -print-log -follow -shard 18 -- -- --keep-data=true'
-
-      - name: Print Volume Used
-        if: always() && steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
-        run: |
-          docker inspect -f '{{ (index .Mounts 0).Name }}' cluster_test_18_$GITHUB_SHA
-
-      - name: Cleanup Docker Volume
-        if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
-        run: |
-          docker rm -v cluster_test_18_$GITHUB_SHA
-
-      - name: Cleanup Docker Container
-        if: always() && steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
-        run: |
-          docker rm -f cluster_test_18_$GITHUB_SHA
-
-      - name: Cleanup Docker Image
-        if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
-        run: |
-          docker image rm cluster_test_18:$GITHUB_SHA
+    - name: Check if workflow needs to be skipped
+      id: skip-workflow
+      run: |
+        skip='false'
+        if [[ "${{github.event.pull_request}}" ==  "" ]] && [[ "${{github.ref}}" != "refs/heads/main" ]] && [[ ! "${{github.ref}}" =~ ^refs/heads/release-[0-9]+\.[0-9]$ ]] && [[ ! "${{github.ref}}" =~ "refs/tags/.*" ]]; then
+          skip='true'
+        fi
+        echo Skip ${skip}
+        echo "::set-output name=skip-workflow::${skip}"
+
+    - name: Check out code
+      if: steps.skip-workflow.outputs.skip-workflow == 'false'
+      uses: actions/checkout@v2
+
+    - name: Check for changes in relevant files
+      if: steps.skip-workflow.outputs.skip-workflow == 'false'
+      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/cluster_endtoend_18.yml'
+
+    - name: Set up Go
+      if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
+      uses: actions/setup-go@v2
+      with:
+        go-version: 1.18.5
+
+    - name: Set up python
+      if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
+      uses: actions/setup-python@v2
+
+    - name: Tune the OS
+      if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
+      run: |
+        echo '1024 65535' | sudo tee -a /proc/sys/net/ipv4/ip_local_port_range
+        # Increase the asynchronous non-blocking I/O. More information at https://dev.mysql.com/doc/refman/5.7/en/innodb-parameters.html#sysvar_innodb_use_native_aio
+        echo "fs.aio-max-nr = 1048576" | sudo tee -a /etc/sysctl.conf
+        sudo sysctl -p /etc/sysctl.conf
+
+    - name: Get dependencies
+      if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
+      run: |
+        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/vitessio/go-junit-report@HEAD
+
+    - name: Installing zookeeper and consul
+      if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
+      run: |
+          make tools
+
+    - name: Setup launchable dependencies
+      if: steps.skip-workflow.outputs.skip-workflow == 'false' && 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.skip-workflow.outputs.skip-workflow == 'false' && 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 18 | tee -a output.txt | go-junit-report -set-exit-code > report.xml
+
+    - name: Print test output and Record test result in launchable
+      if: steps.skip-workflow.outputs.skip-workflow == 'false' && 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/Makefile b/Makefile
index 78ab82f1f6..b65e486a90 100644
--- a/Makefile
+++ b/Makefile
@@ -187,11 +187,11 @@ asthelpergen:
 
 sizegen:
 	go run ./go/tools/sizegen/sizegen.go \
-		-in ./go/... \
-	  	-gen vitess.io/vitess/go/pools.Setting \
-	  	-gen vitess.io/vitess/go/vt/vtgate/engine.Plan \
-	  	-gen vitess.io/vitess/go/vt/vttablet/tabletserver.TabletPlan \
-	  	-gen vitess.io/vitess/go/sqltypes.Result
+		--in ./go/... \
+		--gen vitess.io/vitess/go/pools.Setting \
+		--gen vitess.io/vitess/go/vt/vtgate/engine.Plan \
+		--gen vitess.io/vitess/go/vt/vttablet/tabletserver.TabletPlan \
+		--gen vitess.io/vitess/go/sqltypes.Result
 
 astfmtgen:
 	go run ./go/tools/astfmtgen/main.go vitess.io/vitess/go/vt/sqlparser/...
diff --git a/doc/releasenotes/15_0_0_summary.md b/doc/releasenotes/15_0_0_summary.md
index eec611b9ab..d52da3adb3 100644
--- a/doc/releasenotes/15_0_0_summary.md
+++ b/doc/releasenotes/15_0_0_summary.md
@@ -248,3 +248,66 @@ send semi-sync ACKs. This ensures that any committed write exists in at least 2
 #### FORMAT=vtexplain
 
 With this new `explain` format, you can get an output that is very similar to the command line `vtexplain` app, but from a running `vtgate`, through a MySQL query.
+
+### VTOrc
+
+#### Configuration Renames
+
+VTOrc configurations that had `Orchestrator` as a substring have been renamed to have `VTOrc` instead. The old configuration won't 
+work in this release. So if backward compatibility is desired, then before upgrading it is suggested to duplicate the old configurations
+and also set them for the new configurations.
+
+VTOrc ignores the configurations that it doesn't understand. So new configurations can be added while running the previous release.
+After the upgrade, the old configurations can be dropped.
+
+|           Old Configuration            |        New Configuration        |
+|:--------------------------------------:|:-------------------------------:|
+|         MySQLOrchestratorHost          |         MySQLVTOrcHost          |
+|  MySQLOrchestratorMaxPoolConnections   |  MySQLVTOrcMaxPoolConnections   |
+|         MySQLOrchestratorPort          |         MySQLVTOrcPort          |
+|       MySQLOrchestratorDatabase        |       MySQLVTOrcDatabase        |
+|         MySQLOrchestratorUser          |         MySQLVTOrcUser          |
+|       MySQLOrchestratorPassword        |       MySQLVTOrcPassword        |
+| MySQLOrchestratorCredentialsConfigFile | MySQLVTOrcCredentialsConfigFile |
+|   MySQLOrchestratorSSLPrivateKeyFile   |   MySQLVTOrcSSLPrivateKeyFile   |
+|      MySQLOrchestratorSSLCertFile      |      MySQLVTOrcSSLCertFile      |
+|       MySQLOrchestratorSSLCAFile       |       MySQLVTOrcSSLCAFile       |
+|     MySQLOrchestratorSSLSkipVerify     |     MySQLVTOrcSSLSkipVerify     |
+|     MySQLOrchestratorUseMutualTLS      |     MySQLVTOrcUseMutualTLS      |
+|  MySQLOrchestratorReadTimeoutSeconds   |  MySQLVTOrcReadTimeoutSeconds   |
+|    MySQLOrchestratorRejectReadOnly     |    MySQLVTOrcRejectReadOnly     |
+
+
+For example, if you have the following configuration -
+```json
+{
+  "MySQLOrchestratorHost": "host"
+}
+```
+then, you should change it to
+```json
+{
+  "MySQLOrchestratorHost": "host",
+  "MySQLVTOrcHost": "host"
+}
+```
+while still on the old release. After changing the configuration, you can upgrade vitess.
+After upgrading, the old configurations can be dropped - 
+```json
+{
+  "MySQLVTOrcHost": "host"
+}
+```
+
+#### Default Configuration Files
+
+The default files that VTOrc searches for configurations in have also changed from `"/etc/orchestrator.conf.json", "conf/orchestrator.conf.json", "orchestrator.conf.json"` to
+`"/etc/vtorc.conf.json", "conf/vtorc.conf.json", "vtorc.conf.json"`.
+
+#### Debug Pages in VTOrc
+
+Like the other vitess binaries (`vtgate`, `vttablet`), now `vtorc` also takes a `--port` flag, on which it 
+displays the `/debug` pages including `/debug/status` and variables it tracks on `/debug/vars`.
+
+This change is backward compatible and opt-in by default. Not specifying the flag works like it used to with
+VTOrc running without displaying these pages.
diff --git a/examples/compose/README.md b/examples/compose/README.md
index 5f216e724b..880c3be9ed 100644
--- a/examples/compose/README.md
+++ b/examples/compose/README.md
@@ -152,6 +152,9 @@ vitess/examples/compose$ ./lvtctl.sh Help
 
 - vtorc web ui:
   http://localhost:13000
+
+- vtorc debug ui:
+  http://localhost:13200
   
 - Stream querylog
   `curl -S localhost:15099/debug/querylog`
diff --git a/examples/compose/docker-compose.beginners.yml b/examples/compose/docker-compose.beginners.yml
index d4402c9167..dd56c7e4de 100644
--- a/examples/compose/docker-compose.beginners.yml
+++ b/examples/compose/docker-compose.beginners.yml
@@ -285,10 +285,12 @@ services:
       - vtctld
       - set_keyspace_durability_policy
     ports:
+      - "13200:8080"
       - "13000:3000"
     volumes:
       - ".:/script"
     environment:
+      - WEB_PORT=8080
       - TOPOLOGY_FLAGS
       - WEB_PORT
       - GRPC_PORT
diff --git a/examples/compose/docker-compose.yml b/examples/compose/docker-compose.yml
index 5dd64b8e0a..faa98c816c 100644
--- a/examples/compose/docker-compose.yml
+++ b/examples/compose/docker-compose.yml
@@ -178,11 +178,13 @@ services:
     environment:
     - TOPOLOGY_FLAGS=--topo_implementation consul --topo_global_server_address consul1:8500
       --topo_global_root vitess/global
+    - WEB_PORT=8080
     - EXTERNAL_DB=0
     - DB_USER=
     - DB_PASS=
     image: vitess/lite:${VITESS_TAG:-latest}
     ports:
+    - 13200:8080
     - 13000:3000
     volumes:
     - .:/script
diff --git a/examples/compose/vtcompose/vtcompose.go b/examples/compose/vtcompose/vtcompose.go
index ca50080e0f..3a5561ff27 100644
--- a/examples/compose/vtcompose/vtcompose.go
+++ b/examples/compose/vtcompose/vtcompose.go
@@ -744,15 +744,17 @@ func generateVTOrc(dbInfo externalDbInfo, keyspaceInfoMap map[string]keyspaceInf
     volumes:
       - ".:/script"
     environment:
-      - TOPOLOGY_FLAGS=%[1]s
-      - EXTERNAL_DB=%[2]s
-      - DB_USER=%[3]s
-      - DB_PASS=%[4]s
+      - WEB_PORT=%[1]d
+      - TOPOLOGY_FLAGS=%[2]s
+      - EXTERNAL_DB=%[3]s
+      - DB_USER=%[4]s
+      - DB_PASS=%[5]s
     ports:
       - "13000:3000"
+      - "13200:%[1]d"
     command: ["sh", "-c", "/script/vtorc-up.sh"]
-    %[5]s
-`, opts.topologyFlags, externalDb, dbInfo.dbUser, dbInfo.dbPass, dependsOn)
+    %[6]s
+`, opts.webPort, opts.topologyFlags, externalDb, dbInfo.dbUser, dbInfo.dbPass, dependsOn)
 }
 
 func generateVreplication(dbInfo externalDbInfo, opts vtOptions) string {
diff --git a/examples/compose/vtorc-up.sh b/examples/compose/vtorc-up.sh
index a1d7492efb..3b66d5eacd 100755
--- a/examples/compose/vtorc-up.sh
+++ b/examples/compose/vtorc-up.sh
@@ -17,6 +17,7 @@
 set -u
 
 external=${EXTERNAL_DB:-0}
+web_port=${WEB_PORT:-'8080'}
 config=${VTORC_CONFIG:-/vt/vtorc/config.json}
 # Copy config directory
 cp -R /script/vtorc /vt
@@ -40,5 +41,6 @@ echo "Starting vtorc..."
 exec /vt/bin/vtorc \
 $TOPOLOGY_FLAGS \
 --logtostderr=true \
+--port $web_port \
 --orc_web_dir=/vt/web/vtorc \
 --config $config
diff --git a/examples/local/scripts/vtorc-up.sh b/examples/local/scripts/vtorc-up.sh
index da5df49b97..88187c03cd 100755
--- a/examples/local/scripts/vtorc-up.sh
+++ b/examples/local/scripts/vtorc-up.sh
@@ -5,6 +5,7 @@ source ./env.sh
 log_dir="${VTDATAROOT}/tmp"
 web_dir="../../web/vtorc"
 vtorc_web_port=16000
+port=16001
 
 vtorc \
   $TOPOLOGY_FLAGS \
@@ -12,6 +13,7 @@ vtorc \
   --logtostderr \
   --alsologtostderr \
   --config="./vtorc/config.json" \
+  --port $port \
   > "${log_dir}/vtorc.out" 2>&1 &
 
 vtorc_pid=$!
@@ -20,6 +22,7 @@ echo ${vtorc_pid} > "${log_dir}/vtorc.pid"
 echo "\
 vtorc is running!
   - UI: http://localhost:${vtorc_web_port}
+  - Debug UI: http://localhost:${port}
   - Logs: ${log_dir}/vtorc.out
   - PID: ${vtorc_pid}
 "
diff --git a/examples/region_sharding/scripts/vtorc-up.sh b/examples/region_sharding/scripts/vtorc-up.sh
index da5df49b97..88187c03cd 100755
--- a/examples/region_sharding/scripts/vtorc-up.sh
+++ b/examples/region_sharding/scripts/vtorc-up.sh
@@ -5,6 +5,7 @@ source ./env.sh
 log_dir="${VTDATAROOT}/tmp"
 web_dir="../../web/vtorc"
 vtorc_web_port=16000
+port=16001
 
 vtorc \
   $TOPOLOGY_FLAGS \
@@ -12,6 +13,7 @@ vtorc \
   --logtostderr \
   --alsologtostderr \
   --config="./vtorc/config.json" \
+  --port $port \
   > "${log_dir}/vtorc.out" 2>&1 &
 
 vtorc_pid=$!
@@ -20,6 +22,7 @@ echo ${vtorc_pid} > "${log_dir}/vtorc.pid"
 echo "\
 vtorc is running!
   - UI: http://localhost:${vtorc_web_port}
+  - Debug UI: http://localhost:${port}
   - Logs: ${log_dir}/vtorc.out
   - PID: ${vtorc_pid}
 "
diff --git a/go/cmd/vtorc/index.go b/go/cmd/vtorc/index.go
new file mode 100644
index 0000000000..dcbe1113e5
--- /dev/null
+++ b/go/cmd/vtorc/index.go
@@ -0,0 +1,28 @@
+/*
+Copyright 2022 The Vitess Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package main
+
+import (
+	"net/http"
+)
+
+func init() {
+	// Anything unrecognized gets redirected to the status page.
+	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
+		http.Redirect(w, r, "/debug/status", http.StatusFound)
+	})
+}
diff --git a/go/cmd/vtorc/main.go b/go/cmd/vtorc/main.go
index a8a8d33c5c..cc9aee2cc5 100644
--- a/go/cmd/vtorc/main.go
+++ b/go/cmd/vtorc/main.go
@@ -25,7 +25,6 @@ import (
 	_ "github.com/mattn/go-sqlite3"
 	"github.com/spf13/pflag"
 
-	_flag "vitess.io/vitess/go/internal/flag"
 	"vitess.io/vitess/go/vt/grpccommon"
 	"vitess.io/vitess/go/vt/log"
 	vtlog "vitess.io/vitess/go/vt/log"
@@ -81,7 +80,7 @@ func transformArgsForPflag(fs *pflag.FlagSet, args []string) (result []string) {
 			// In the latter case, we don't need to do any transformations, but
 			// in the former, we do.
 			name := strings.SplitN(arg[1:], "=", 2)[0] // discard any potential value (`-myflag` and `-myflag=10` both have the name of `myflag`)
-			if fs.Lookup(name) != nil {
+			if fs.Lookup(name) != nil || name == "help" {
 				// Case 1: We have a long opt with this name, so we need to
 				// prepend an additional hyphen.
 				result = append(result, "-"+arg)
@@ -106,23 +105,12 @@ func main() {
 	grpccommon.RegisterFlags(fs)
 	vtlog.RegisterFlags(fs)
 	logutil.RegisterFlags(fs)
+	servenv.RegisterDefaultFlags()
+	servenv.RegisterFlags()
+	servenv.OnParseFor("vtorc", func(flags *pflag.FlagSet) { flags.AddFlagSet(fs) })
 
 	args := append([]string{}, os.Args...)
 	os.Args = os.Args[0:1]
-	servenv.ParseFlags("vtorc")
-
-	// N.B. This code has to be duplicated from go/internal/flag in order to
-	// correctly transform `-help` => `--help`. Otherwise passing `-help` would
-	// produce:
-	//	unknown shorthand flag: 'e' in -elp
-	var help bool
-	if fs.Lookup("help") == nil {
-		if fs.ShorthandLookup("h") == nil {
-			fs.BoolVarP(&help, "help", "h", false, "display usage and exit")
-		} else {
-			fs.BoolVar(&help, "help", false, "display usage and exit")
-		}
-	}
 
 	configFile := fs.String("config", "", "config file name")
 	sibling := fs.StringP("sibling", "s", "", "sibling instance, host_fqdn[:port]")
@@ -151,12 +139,7 @@ Please update your scripts before the next version, when this will begin to brea
 		log.Warningf(warning, args, os.Args)
 	}
 
-	_flag.Parse(fs)
-	// N.B. Also duplicated from go/internal/flag, for the same reason as above.
-	if help || fs.Arg(0) == "help" {
-		pflag.Usage()
-		os.Exit(0)
-	}
+	servenv.ParseFlags("vtorc")
 
 	if *destination != "" && *sibling != "" {
 		log.Fatalf("-s and -d are synonyms, yet both were specified. You're probably doing the wrong thing.")
@@ -198,5 +181,15 @@ Please update your scripts before the next version, when this will begin to brea
 	config.RuntimeCLIFlags.ConfiguredVersion = AppVersion
 	config.MarkConfigurationLoaded()
 
-	app.HTTP(*discovery)
+	go app.HTTP(*discovery)
+
+	servenv.OnRun(func() {
+		addStatusParts()
+	})
+
+	// For backward compatability, we require that VTOrc functions even when the --port flag is not provided.
+	// In this case, it should function like before but without the servenv pages.
+	// Therefore, currently we don't check for the --port flag to be necessary, but release 16+ that check
+	// can be added to always have the serenv page running in VTOrc.
+	servenv.RunDefault()
 }
diff --git a/go/cmd/vtorc/plugin_prometheusbackend.go b/go/cmd/vtorc/plugin_prometheusbackend.go
new file mode 100644
index 0000000000..868e097ade
--- /dev/null
+++ b/go/cmd/vtorc/plugin_prometheusbackend.go
@@ -0,0 +1,31 @@
+/*
+Copyright 2022 The Vitess Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package 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("vtorc")
+	})
+}
diff --git a/go/cmd/vtorc/status.go b/go/cmd/vtorc/status.go
new file mode 100644
index 0000000000..bdb5496305
--- /dev/null
+++ b/go/cmd/vtorc/status.go
@@ -0,0 +1,30 @@
+/*
+Copyright 2022 The Vitess Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or 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
+
+import (
+	"vitess.io/vitess/go/vt/servenv"
+	"vitess.io/vitess/go/vt/vtorc/logic"
+)
+
+// addStatusParts adds UI parts to the /debug/status page of VTOrc
+func addStatusParts() {
+	servenv.AddStatusPart("Recent Recoveries", logic.TopologyRecoveriesTemplate, func() any {
+		recoveries, _ := logic.ReadRecentRecoveries("", false, 0)
+		return recoveries
+	})
+}
diff --git a/go/flags/endtoend/vtorc.txt b/go/flags/endtoend/vtorc.txt
new file mode 100644
index 0000000000..49354958c2
--- /dev/null
+++ b/go/flags/endtoend/vtorc.txt
@@ -0,0 +1,82 @@
+Usage of vtorc:
+      --alsologtostderr                          log to standard error as well as files
+      --binlog string                            Binary log file name
+      --catch-sigpipe                            catch and ignore SIGPIPE on stdout and stderr if specified
+      --clusters_to_watch string                 Comma-separated list of keyspaces or keyspace/shards that this instance will monitor and repair. Defaults to all clusters in the topology. Example: "ks1,ks2/-80"
+      --config string                            config file name
+  -d, --destination string                       destination instance, host_fqdn[:port] (synonym to -s)
+      --discovery                                auto discovery mode (default true)
+      --emit_stats                               If set, emit stats to push-based monitoring and stats backends
+      --enable-database-update                   Enable database update, overrides SkipVTOrcDatabaseUpdate
+      --grab-election                            Grab leadership (only applies to continuous mode)
+      --grpc_auth_static_client_creds string     when using grpc_static_auth in the server, this file provides the credentials to use to authenticate with server
+      --grpc_compression string                  Which protocol to use for compressing gRPC. Default: nothing. Supported: snappy
+      --grpc_enable_tracing                      Enable gRPC tracing.
+      --grpc_initial_conn_window_size int        gRPC initial connection window size
+      --grpc_initial_window_size int             gRPC initial window size
+      --grpc_keepalive_time duration             After a duration of this time, if the client doesn't see any activity, it pings the server to see if the transport is still alive. (default 10s)
+      --grpc_keepalive_timeout duration          After having pinged for keepalive check, the client waits for a duration of Timeout and if no activity is seen even after that the connection is closed. (default 10s)
+      --grpc_max_message_size int                Maximum allowed RPC message size. Larger messages will be rejected by gRPC with the error 'exceeding the max size'. (default 16777216)
+      --grpc_prometheus                          Enable gRPC monitoring with Prometheus.
+  -h, --help                                     display usage and exit
+      --ignore-raft-setup                        Override RaftEnabled for CLI invocation (CLI by default not allowed for raft setups). NOTE: operations by CLI invocation may not reflect in all raft nodes.
+      --keep_logs duration                       keep logs for this long (using ctime) (zero to keep forever)
+      --keep_logs_by_mtime duration              keep logs for this long (using mtime) (zero to keep forever)
+      --lameduck-period duration                 keep running at least this long after SIGTERM before stopping (default 50ms)
+      --log_backtrace_at traceLocation           when logging hits line file:N, emit a stack trace (default :0)
+      --log_dir string                           If non-empty, write log files in this directory
+      --log_err_stacks                           log stack traces for errors
+      --log_rotate_max_size uint                 size in bytes at which logs are rotated (glog.MaxSize) (default 1887436800)
+      --logtostderr                              log to standard error instead of files
+      --mysql_server_flush_delay duration        Delay after which buffered response will be flushed to the client. (default 100ms)
+      --noop                                     Dry run; do not perform destructing operations
+      --onclose_timeout duration                 wait no more than this for OnClose handlers before stopping (default 1ns)
+      --onterm_timeout duration                  wait no more than this for OnTermSync handlers before stopping (default 10s)
+      --orc_web_dir string                       VTOrc http file location (default "web/vtorc")
+      --pid_file string                          If set, the process will write its pid to the named file, and delete it on graceful shutdown.
+      --port int                                 port for the server
+      --pprof strings                            enable profiling
+      --promotion-rule string                    Promotion rule for register-andidate (prefer|neutral|prefer_not|must_not) (default "prefer")
+      --purge_logs_interval duration             how often try to remove old logs (default 1h0m0s)
+      --remote_operation_timeout duration        time to wait for a remote operation (default 30s)
+      --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)
+      --shutdown_wait_time duration              maximum time to wait for vtorc to release all the locks that it is holding before shutting down on SIGTERM (default 30s)
+  -s, --sibling string                           sibling instance, host_fqdn[:port]
+      --skip-continuous-registration             Skip cli commands performaing continuous registration (to reduce orchestratrator backend db load
+      --skip-unresolve                           Do not unresolve a host name
+      --skip-unresolve-check                     Skip/ignore checking an unresolve mapping (via hostname_unresolve table) resolves back to same hostname
+      --statement string                         Statement/hint
+      --stats_backend string                     The name of the registered push-based monitoring/stats backend to use
+      --stats_combine_dimensions string          List of dimensions to be combined into a single "all" value in exported stats vars
+      --stats_common_tags string                 Comma-separated list of common tags for the stats backend. It provides both label and values. Example: label1:value1,label2:value2
+      --stats_drop_variables string              Variables to be dropped from the list of exported variables.
+      --stats_emit_period duration               Interval between emitting stats to all registered backends (default 1m0s)
+      --stderrthreshold severity                 logs at or above this threshold go to stderr (default 1)
+      --tablet_manager_grpc_ca string            the server ca to use to validate servers when connecting
+      --tablet_manager_grpc_cert string          the cert to use to connect
+      --tablet_manager_grpc_concurrency int      concurrency to use to talk to a vttablet server for performance-sensitive RPCs (like ExecuteFetchAs{Dba,AllPrivs,App}) (default 8)
+      --tablet_manager_grpc_connpool_size int    number of tablets to keep tmclient connections open to (default 100)
+      --tablet_manager_grpc_crl string           the server crl to use to validate server certificates when connecting
+      --tablet_manager_grpc_key string           the key to use to connect
+      --tablet_manager_grpc_server_name string   the server name to use to validate server certificate
+      --tablet_manager_protocol string           Protocol to use to make tabletmanager RPCs to vttablets. (default "grpc")
+      --tag string                               tag to add ('tagname' or 'tagname=tagvalue') or to search ('tagname' or 'tagname=tagvalue' or comma separated 'tag0,tag1=val1,tag2' for intersection of all)
+      --topo_etcd_lease_ttl int                  Lease TTL for locks and leader election. The client will use KeepAlive to keep the lease going. (default 30)
+      --topo_etcd_tls_ca string                  path to the ca to use to validate the server cert when connecting to the etcd topo server
+      --topo_etcd_tls_cert string                path to the client cert to use to connect to the etcd topo server, requires topo_etcd_tls_key, enables TLS
+      --topo_etcd_tls_key string                 path to the client key to use to connect to the etcd topo server, enables TLS
+      --topo_global_root string                  the path of the global topology data in the global topology server
+      --topo_global_server_address string        the address of the global topology server
+      --topo_implementation string               the topology implementation to use
+      --topo_k8s_context string                  The kubeconfig context to use, overrides the 'current-context' from the config
+      --topo_k8s_kubeconfig string               Path to a valid kubeconfig file. When running as a k8s pod inside the same cluster you wish to use as the topo, you may omit this and the below arguments, and Vitess is capable of auto-discovering the correct values. https://kubernetes.io/docs/tasks/access-application-cluster/access-cluster/#accessing-the-api-from-a-pod
+      --topo_k8s_namespace string                The kubernetes namespace to use for all objects. Default comes from the context or in-cluster config
+      --topo_zk_auth_file string                 auth to use when connecting to the zk topo server, file contents should be <scheme>:<auth>, e.g., digest:user:pass
+      --topo_zk_base_timeout duration            zk base timeout (see zk.Connect) (default 30s)
+      --topo_zk_max_concurrency int              maximum number of pending requests to send to a Zookeeper server. (default 64)
+      --topo_zk_tls_ca string                    the server ca to use to validate servers when connecting to the zk topo server
+      --topo_zk_tls_cert string                  the cert to use to connect to the zk topo server, requires topo_zk_tls_key, enables TLS
+      --topo_zk_tls_key string                   the key to use to connect to the zk topo server, enables TLS
+  -v, --v Level                                  log level for V logs
+      --version                                  print binary version
+      --vmodule moduleSpec                       comma-separated list of pattern=N settings for file-filtered logging
diff --git a/go/tools/sizegen/sizegen.go b/go/tools/sizegen/sizegen.go
index 41519daee8..63ef88dd07 100644
--- a/go/tools/sizegen/sizegen.go
+++ b/go/tools/sizegen/sizegen.go
@@ -18,7 +18,6 @@ package main
 
 import (
 	"bytes"
-	"flag"
 	"fmt"
 	"go/types"
 	"log"
@@ -27,12 +26,13 @@ import (
 	"sort"
 	"strings"
 
+	"github.com/dave/jennifer/jen"
+	"github.com/spf13/pflag"
+	"golang.org/x/tools/go/packages"
+
 	"vitess.io/vitess/go/hack"
 	"vitess.io/vitess/go/tools/common"
 	"vitess.io/vitess/go/tools/goimports"
-
-	"github.com/dave/jennifer/jen"
-	"golang.org/x/tools/go/packages"
 )
 
 const licenseFileHeader = `Copyright 2021 The Vitess Authors.
@@ -495,14 +495,15 @@ func (t *typePaths) Set(path string) error {
 }
 
 func main() {
-	var patterns typePaths
-	var generate typePaths
-	var verify bool
-
-	flag.Var(&patterns, "in", "Go packages to load the generator")
-	flag.Var(&generate, "gen", "Typename of the Go struct to generate size info for")
-	flag.BoolVar(&verify, "verify", false, "ensure that the generated files are correct")
-	flag.Parse()
+	var (
+		patterns, generate []string
+		verify             bool
+	)
+
+	pflag.StringSliceVar(&patterns, "in", nil, "Go packages to load the generator")
+	pflag.StringSliceVar(&generate, "gen", nil, "Typename of the Go struct to generate size info for")
+	pflag.BoolVar(&verify, "verify", false, "ensure that the generated files are correct")
+	pflag.Parse()
 
 	result, err := GenerateSizeHelpers(patterns, generate)
 	if err != nil {
diff --git a/go/vt/vtctl/grpcvtctldclient/client.go b/go/vt/vtctl/grpcvtctldclient/client.go
index 2dc57a546f..497867aebb 100644
--- a/go/vt/vtctl/grpcvtctldclient/client.go
+++ b/go/vt/vtctl/grpcvtctldclient/client.go
@@ -46,7 +46,7 @@ type gRPCVtctldClient struct {
 }
 
 //go:generate -command grpcvtctldclient go run ../vtctldclient/codegen
-//go:generate grpcvtctldclient -out client_gen.go
+//go:generate grpcvtctldclient --out client_gen.go
 
 func gRPCVtctldClientFactory(addr string) (vtctldclient.VtctldClient, error) {
 	opt, err := grpcclientcommon.SecureDialOption()
diff --git a/go/vt/vtctl/localvtctldclient/client.go b/go/vt/vtctl/localvtctldclient/client.go
index b870247fe0..f94f112403 100644
--- a/go/vt/vtctl/localvtctldclient/client.go
+++ b/go/vt/vtctl/localvtctldclient/client.go
@@ -38,7 +38,7 @@ type localVtctldClient struct {
 func (client *localVtctldClient) Close() error { return nil }
 
 //go:generate -command localvtctldclient go run ../vtctldclient/codegen
-//go:generate localvtctldclient -targetpkg localvtctldclient -impl localVtctldClient -out client_gen.go -local
+//go:generate localvtctldclient --targetpkg localvtctldclient --impl localVtctldClient --out client_gen.go --local
 
 // New returns a local vtctldclient.VtctldClient that makes method calls on the
 // provided VtctldServer implementation. No network traffic takes place between
diff --git a/go/vt/vtctl/reparentutil/planned_reparenter.go b/go/vt/vtctl/reparentutil/planned_reparenter.go
index 4f46d234e2..f95329aa25 100644
--- a/go/vt/vtctl/reparentutil/planned_reparenter.go
+++ b/go/vt/vtctl/reparentutil/planned_reparenter.go
@@ -184,6 +184,21 @@ func (pr *PlannedReparenter) preflightChecks(
 		return true, vterrors.Errorf(vtrpc.Code_FAILED_PRECONDITION, "primary-elect tablet %v is not in the shard", primaryElectAliasStr)
 	}
 
+	// PRS is only meant to be called when all the tablets are healthy.
+	// So we assume that all the tablets are reachable and check if the primary elect will be able
+	// to make progress if it is promoted. This is needed because sometimes users may ask to promote
+	// a tablet which can never make progress. For example, let's say the user has a durability policy
+	// where they require 2 semi-sync acks but from cross-cell replicas.
+	// Let's say they have 3 replicas A in zone 1 and B and C in zone 2. In this case, A is the only
+	// eligible primary elect. Both B and C won't be able to make forward progress if they are promoted.
+	var tabletsReachable []*topodatapb.Tablet
+	for _, info := range tabletMap {
+		tabletsReachable = append(tabletsReachable, info.Tablet)
+	}
+	if !canEstablishForTablet(opts.durability, newPrimaryTabletInfo.Tablet, tabletsReachable) {
+		return true, vterrors.Errorf(vtrpc.Code_FAILED_PRECONDITION, "primary-elect tablet %v won't be able to make forward progress on promotion", primaryElectAliasStr)
+	}
+
 	ev.NewPrimary = proto.Clone(newPrimaryTabletInfo.Tablet).(*topodatapb.Tablet)
 
 	return false, nil
diff --git a/go/vt/vtctl/vtctldclient/codegen/main.go b/go/vt/vtctl/vtctldclient/codegen/main.go
index 0aec3ad520..baeb7af8d0 100644
--- a/go/vt/vtctl/vtctldclient/codegen/main.go
+++ b/go/vt/vtctl/vtctldclient/codegen/main.go
@@ -18,7 +18,6 @@ package main
 
 import (
 	"errors"
-	"flag"
 	"fmt"
 	"go/types"
 	"io"
@@ -27,33 +26,34 @@ import (
 	"regexp"
 	"sort"
 
+	"github.com/spf13/pflag"
 	"golang.org/x/tools/go/packages"
 )
 
 func main() { // nolint:funlen
-	source := flag.String("source", "../../proto/vtctlservice", "source package")
-	typeName := flag.String("type", "VtctldClient", "interface type to implement")
-	implType := flag.String("impl", "gRPCVtctldClient", "type implementing the interface")
-	pkgName := flag.String("targetpkg", "grpcvtctldclient", "package name to generate code for")
-	local := flag.Bool("local", false, "generate a local, in-process client rather than a grpcclient")
-	out := flag.String("out", "", "output destination. leave empty to use stdout")
+	source := pflag.String("source", "../../proto/vtctlservice", "source package")
+	typeName := pflag.String("type", "VtctldClient", "interface type to implement")
+	implType := pflag.String("impl", "gRPCVtctldClient", "type implementing the interface")
+	pkgName := pflag.String("targetpkg", "grpcvtctldclient", "package name to generate code for")
+	local := pflag.Bool("local", false, "generate a local, in-process client rather than a grpcclient")
+	out := pflag.String("out", "", "output destination. leave empty to use stdout")
 
-	flag.Parse()
+	pflag.Parse()
 
 	if *source == "" {
-		panic("-source cannot be empty")
+		panic("--source cannot be empty")
 	}
 
 	if *typeName == "" {
-		panic("-type cannot be empty")
+		panic("--type cannot be empty")
 	}
 
 	if *implType == "" {
-		panic("-impl cannot be empty")
+		panic("--impl cannot be empty")
 	}
 
 	if *pkgName == "" {
-		panic("-targetpkg cannot be empty")
+		panic("--targetpkg cannot be empty")
 	}
 
 	var output io.Writer = os.Stdout
diff --git a/go/vt/vtgate/semantics/binder.go b/go/vt/vtgate/semantics/binder.go
index 5045a7a310..543aa53539 100644
--- a/go/vt/vtgate/semantics/binder.go
+++ b/go/vt/vtgate/semantics/binder.go
@@ -114,18 +114,32 @@ func (b *binder) up(cursor *sqlparser.Cursor) error {
 			b.typer.setTypeFor(node, *deps.typ)
 		}
 	case *sqlparser.CountStar:
-		scope := b.scoper.currentScope()
-		var ts TableSet
-		for _, table := range scope.tables {
-			expr := table.getExpr()
+		b.bindCountStar(node)
+	}
+	return nil
+}
+
+func (b *binder) bindCountStar(node *sqlparser.CountStar) {
+	scope := b.scoper.currentScope()
+	var ts TableSet
+	for _, tbl := range scope.tables {
+		switch tbl := tbl.(type) {
+		case *vTableInfo:
+			for _, col := range tbl.cols {
+				if sqlparser.EqualsExpr(node, col) {
+					ts.MergeInPlace(b.recursive[col])
+				}
+			}
+		default:
+			expr := tbl.getExpr()
 			if expr != nil {
-				ts.MergeInPlace(b.tc.tableSetFor(expr))
+				setFor := b.tc.tableSetFor(expr)
+				ts.MergeInPlace(setFor)
 			}
 		}
-		b.recursive[node] = ts
-		b.direct[node] = ts
 	}
-	return nil
+	b.recursive[node] = ts
+	b.direct[node] = ts
 }
 
 func (b *binder) rewriteJoinUsingColName(deps dependency, node *sqlparser.ColName, currentScope *scope) (dependency, error) {
diff --git a/go/vt/vtgate/semantics/early_rewriter.go b/go/vt/vtgate/semantics/early_rewriter.go
index e57828f5bb..6f006efd41 100644
--- a/go/vt/vtgate/semantics/early_rewriter.go
+++ b/go/vt/vtgate/semantics/early_rewriter.go
@@ -33,33 +33,19 @@ type earlyRewriter struct {
 
 func (r *earlyRewriter) down(cursor *sqlparser.Cursor) error {
 	switch node := cursor.Node().(type) {
+	case *sqlparser.Where:
+		if node.Type != sqlparser.HavingClause {
+			return nil
+		}
+		rewriteHavingAndOrderBy(cursor, node)
 	case sqlparser.SelectExprs:
 		_, isSel := cursor.Parent().(*sqlparser.Select)
 		if !isSel {
 			return nil
 		}
-		currentScope := r.scoper.currentScope()
-		var selExprs sqlparser.SelectExprs
-		changed := false
-		for _, selectExpr := range node {
-			starExpr, isStarExpr := selectExpr.(*sqlparser.StarExpr)
-			if !isStarExpr {
-				selExprs = append(selExprs, selectExpr)
-				continue
-			}
-			starExpanded, colNames, err := expandTableColumns(starExpr, currentScope.tables, r.binder.usingJoinInfo, r.scoper.org)
-			if err != nil {
-				return err
-			}
-			if !starExpanded || colNames == nil {
-				selExprs = append(selExprs, selectExpr)
-				continue
-			}
-			selExprs = append(selExprs, colNames...)
-			changed = true
-		}
-		if changed {
-			cursor.ReplaceAndRevisit(selExprs)
+		err := r.expandStar(cursor, node)
+		if err != nil {
+			return err
 		}
 	case *sqlparser.JoinTableExpr:
 		if node.Join == sqlparser.StraightJoinType {
@@ -68,6 +54,7 @@ func (r *earlyRewriter) down(cursor *sqlparser.Cursor) error {
 		}
 	case *sqlparser.Order:
 		r.clause = "order clause"
+		rewriteHavingAndOrderBy(cursor, node)
 	case sqlparser.GroupBy:
 		r.clause = "group statement"
 
@@ -95,6 +82,60 @@ func (r *earlyRewriter) down(cursor *sqlparser.Cursor) error {
 	return nil
 }
 
+func (r *earlyRewriter) expandStar(cursor *sqlparser.Cursor, node sqlparser.SelectExprs) error {
+	currentScope := r.scoper.currentScope()
+	var selExprs sqlparser.SelectExprs
+	changed := false
+	for _, selectExpr := range node {
+		starExpr, isStarExpr := selectExpr.(*sqlparser.StarExpr)
+		if !isStarExpr {
+			selExprs = append(selExprs, selectExpr)
+			continue
+		}
+		starExpanded, colNames, err := expandTableColumns(starExpr, currentScope.tables, r.binder.usingJoinInfo, r.scoper.org)
+		if err != nil {
+			return err
+		}
+		if !starExpanded || colNames == nil {
+			selExprs = append(selExprs, selectExpr)
+			continue
+		}
+		selExprs = append(selExprs, colNames...)
+		changed = true
+	}
+	if changed {
+		cursor.ReplaceAndRevisit(selExprs)
+	}
+	return nil
+}
+
+func rewriteHavingAndOrderBy(cursor *sqlparser.Cursor, node sqlparser.SQLNode) {
+	sel, isSel := cursor.Parent().(*sqlparser.Select)
+	if !isSel {
+		return
+	}
+	sqlparser.Rewrite(node, func(inner *sqlparser.Cursor) bool {
+		switch col := inner.Node().(type) {
+		case *sqlparser.Subquery:
+			return false
+		case *sqlparser.ColName:
+			if !col.Qualifier.IsEmpty() {
+				return false
+			}
+			for _, e := range sel.SelectExprs {
+				ae, ok := e.(*sqlparser.AliasedExpr)
+				if !ok {
+					continue
+				}
+				if ae.As.Equal(col.Name) {
+					inner.Replace(ae.Expr)
+				}
+			}
+		}
+		return true
+	}, nil)
+}
+
 func (r *earlyRewriter) rewriteOrderByExpr(node *sqlparser.Literal) (sqlparser.Expr, error) {
 	currScope, found := r.scoper.specialExprScopes[node]
 	if !found {
diff --git a/go/vt/vtorc/app/http.go b/go/vt/vtorc/app/http.go
index de689d1ca6..3b4e848471 100644
--- a/go/vt/vtorc/app/http.go
+++ b/go/vt/vtorc/app/http.go
@@ -171,5 +171,5 @@ func standardHTTP(continuousDiscovery bool) {
 			log.Fatal(err)
 		}
 	}
-	log.Info("Web server started")
+	log.Info("Web server closed")
 }
diff --git a/go/vt/vtorc/config/config.go b/go/vt/vtorc/config/config.go
index d98d178a74..d8102a679e 100644
--- a/go/vt/vtorc/config/config.go
+++ b/go/vt/vtorc/config/config.go
@@ -42,19 +42,14 @@ var configurationLoaded = make(chan bool)
 
 const (
 	HealthPollSeconds                     = 1
-	RaftHealthPollSeconds                 = 10
-	RecoveryPollSeconds                   = 1
 	ActiveNodeExpireSeconds               = 5
-	BinlogFileHistoryDays                 = 1
 	MaintenanceOwner                      = "vtorc"
 	AuditPageSize                         = 20
 	MaintenancePurgeDays                  = 7
 	MySQLTopologyMaxPoolConnections       = 3
 	MaintenanceExpireMinutes              = 10
-	AgentHTTPTimeoutSeconds               = 60
 	DebugMetricsIntervalSeconds           = 10
 	StaleInstanceCoordinatesExpireSeconds = 60
-	SelectTrueQuery                       = "select 1"
 )
 
 // Configuration makes for vtorc configuration input, which can be provided by user via JSON formatted file.
@@ -91,20 +86,20 @@ type Configuration struct {
 	DefaultRaftPort                             int      // if a RaftNodes entry does not specify port, use this one
 	RaftNodes                                   []string // Raft nodes to make initial connection with
 	ExpectFailureAnalysisConcensus              bool
-	MySQLOrchestratorHost                       string
-	MySQLOrchestratorMaxPoolConnections         int // The maximum size of the connection pool to the VTOrc backend.
-	MySQLOrchestratorPort                       uint
-	MySQLOrchestratorDatabase                   string
-	MySQLOrchestratorUser                       string
-	MySQLOrchestratorPassword                   string
-	MySQLOrchestratorCredentialsConfigFile      string   // my.cnf style configuration file from where to pick credentials. Expecting `user`, `password` under `[client]` section
-	MySQLOrchestratorSSLPrivateKeyFile          string   // Private key file used to authenticate with the VTOrc mysql instance with TLS
-	MySQLOrchestratorSSLCertFile                string   // Certificate PEM file used to authenticate with the VTOrc mysql instance with TLS
-	MySQLOrchestratorSSLCAFile                  string   // Certificate Authority PEM file used to authenticate with the VTOrc mysql instance with TLS
-	MySQLOrchestratorSSLSkipVerify              bool     // If true, do not strictly validate mutual TLS certs for the VTOrc mysql instances
-	MySQLOrchestratorUseMutualTLS               bool     // Turn on TLS authentication with the VTOrc MySQL instance
-	MySQLOrchestratorReadTimeoutSeconds         int      // Number of seconds before backend mysql read operation is aborted (driver-side)
-	MySQLOrchestratorRejectReadOnly             bool     // Reject read only connections https://github.com/go-sql-driver/mysql#rejectreadonly
+	MySQLVTOrcHost                              string
+	MySQLVTOrcMaxPoolConnections                int // The maximum size of the connection pool to the VTOrc backend.
+	MySQLVTOrcPort                              uint
+	MySQLVTOrcDatabase                          string
+	MySQLVTOrcUser                              string
+	MySQLVTOrcPassword                          string
+	MySQLVTOrcCredentialsConfigFile             string   // my.cnf style configuration file from where to pick credentials. Expecting `user`, `password` under `[client]` section
+	MySQLVTOrcSSLPrivateKeyFile                 string   // Private key file used to authenticate with the VTOrc mysql instance with TLS
+	MySQLVTOrcSSLCertFile                       string   // Certificate PEM file used to authenticate with the VTOrc mysql instance with TLS
+	MySQLVTOrcSSLCAFile                         string   // Certificate Authority PEM file used to authenticate with the VTOrc mysql instance with TLS
+	MySQLVTOrcSSLSkipVerify                     bool     // If true, do not strictly validate mutual TLS certs for the VTOrc mysql instances
+	MySQLVTOrcUseMutualTLS                      bool     // Turn on TLS authentication with the VTOrc MySQL instance
+	MySQLVTOrcReadTimeoutSeconds                int      // Number of seconds before backend mysql read operation is aborted (driver-side)
+	MySQLVTOrcRejectReadOnly                    bool     // Reject read only connections https://github.com/go-sql-driver/mysql#rejectreadonly
 	MySQLConnectTimeoutSeconds                  int      // Number of seconds before connection is aborted (driver-side)
 	MySQLDiscoveryReadTimeoutSeconds            int      // Number of seconds before topology mysql read operation is aborted (driver-side). Used for discovery queries.
 	MySQLTopologyReadTimeoutSeconds             int      // Number of seconds before topology mysql read operation is aborted (driver-side). Used for all but discovery queries.
@@ -227,6 +222,8 @@ type Configuration struct {
 	InstanceDBExecContextTimeoutSeconds         int               // Timeout on context used while calling ExecContext on instance database
 	LockShardTimeoutSeconds                     int               // Timeout on context used to lock shard. Should be a small value because we should fail-fast
 	WaitReplicasTimeoutSeconds                  int               // Timeout on amount of time to wait for the replicas in case of ERS. Should be a small value because we should fail-fast. Should not be larger than LockShardTimeoutSeconds since that is the total time we use for an ERS.
+	TopoInformationRefreshSeconds               int               // Timer duration on which VTOrc refreshes the keyspace and vttablet records from the topo-server.
+	RecoveryPollSeconds                         int               // Timer duration on which VTOrc recovery analysis runs
 }
 
 // ToJSONString will marshal this configuration as JSON
@@ -259,14 +256,14 @@ func newConfiguration() *Configuration {
 		DefaultRaftPort:                             10008,
 		RaftNodes:                                   []string{},
 		ExpectFailureAnalysisConcensus:              true,
-		MySQLOrchestratorMaxPoolConnections:         128, // limit concurrent conns to backend DB
-		MySQLOrchestratorPort:                       3306,
+		MySQLVTOrcMaxPoolConnections:                128, // limit concurrent conns to backend DB
+		MySQLVTOrcPort:                              3306,
 		MySQLTopologyUseMutualTLS:                   false,
 		MySQLTopologyUseMixedTLS:                    true,
-		MySQLOrchestratorUseMutualTLS:               false,
+		MySQLVTOrcUseMutualTLS:                      false,
 		MySQLConnectTimeoutSeconds:                  2,
-		MySQLOrchestratorReadTimeoutSeconds:         30,
-		MySQLOrchestratorRejectReadOnly:             false,
+		MySQLVTOrcReadTimeoutSeconds:                30,
+		MySQLVTOrcRejectReadOnly:                    false,
 		MySQLDiscoveryReadTimeoutSeconds:            10,
 		MySQLTopologyReadTimeoutSeconds:             600,
 		MySQLConnectionLifetimeSeconds:              0,
@@ -378,32 +375,34 @@ func newConfiguration() *Configuration {
 		InstanceDBExecContextTimeoutSeconds:         30,
 		LockShardTimeoutSeconds:                     30,
 		WaitReplicasTimeoutSeconds:                  30,
+		TopoInformationRefreshSeconds:               15,
+		RecoveryPollSeconds:                         1,
 	}
 }
 
 func (config *Configuration) postReadAdjustments() error {
-	if config.MySQLOrchestratorCredentialsConfigFile != "" {
+	if config.MySQLVTOrcCredentialsConfigFile != "" {
 		mySQLConfig := struct {
 			Client struct {
 				User     string
 				Password string
 			}
 		}{}
-		err := gcfg.ReadFileInto(&mySQLConfig, config.MySQLOrchestratorCredentialsConfigFile)
+		err := gcfg.ReadFileInto(&mySQLConfig, config.MySQLVTOrcCredentialsConfigFile)
 		if err != nil {
 			log.Fatalf("Failed to parse gcfg data from file: %+v", err)
 		} else {
-			log.Infof("Parsed vtorc credentials from %s", config.MySQLOrchestratorCredentialsConfigFile)
-			config.MySQLOrchestratorUser = mySQLConfig.Client.User
-			config.MySQLOrchestratorPassword = mySQLConfig.Client.Password
+			log.Infof("Parsed vtorc credentials from %s", config.MySQLVTOrcCredentialsConfigFile)
+			config.MySQLVTOrcUser = mySQLConfig.Client.User
+			config.MySQLVTOrcPassword = mySQLConfig.Client.Password
 		}
 	}
 	{
 		// We accept password in the form "${SOME_ENV_VARIABLE}" in which case we pull
 		// the given variable from os env
-		submatch := envVariableRegexp.FindStringSubmatch(config.MySQLOrchestratorPassword)
+		submatch := envVariableRegexp.FindStringSubmatch(config.MySQLVTOrcPassword)
 		if len(submatch) > 1 {
-			config.MySQLOrchestratorPassword = os.Getenv(submatch[1])
+			config.MySQLVTOrcPassword = os.Getenv(submatch[1])
 		}
 	}
 	if config.MySQLTopologyCredentialsConfigFile != "" {
diff --git a/go/vt/vtorc/db/db.go b/go/vt/vtorc/db/db.go
index 19fa3617c6..ba25fab705 100644
--- a/go/vt/vtorc/db/db.go
+++ b/go/vt/vtorc/db/db.go
@@ -67,16 +67,16 @@ func getMySQLURI() string {
 		return mysqlURI
 	}
 	mysqlURI := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?timeout=%ds&readTimeout=%ds&rejectReadOnly=%t&interpolateParams=true",
-		config.Config.MySQLOrchestratorUser,
-		config.Config.MySQLOrchestratorPassword,
-		config.Config.MySQLOrchestratorHost,
-		config.Config.MySQLOrchestratorPort,
-		config.Config.MySQLOrchestratorDatabase,
+		config.Config.MySQLVTOrcUser,
+		config.Config.MySQLVTOrcPassword,
+		config.Config.MySQLVTOrcHost,
+		config.Config.MySQLVTOrcPort,
+		config.Config.MySQLVTOrcDatabase,
 		config.Config.MySQLConnectTimeoutSeconds,
-		config.Config.MySQLOrchestratorReadTimeoutSeconds,
-		config.Config.MySQLOrchestratorRejectReadOnly,
+		config.Config.MySQLVTOrcReadTimeoutSeconds,
+		config.Config.MySQLVTOrcRejectReadOnly,
 	)
-	if config.Config.MySQLOrchestratorUseMutualTLS {
+	if config.Config.MySQLVTOrcUseMutualTLS {
 		mysqlURI, _ = SetupMySQLVTOrcTLS(mysqlURI)
 	}
 	return mysqlURI
@@ -122,14 +122,14 @@ func openTopology(host string, port int, readTimeout int) (db *sql.DB, err error
 
 func openOrchestratorMySQLGeneric() (db *sql.DB, fromCache bool, err error) {
 	uri := fmt.Sprintf("%s:%s@tcp(%s:%d)/?timeout=%ds&readTimeout=%ds&interpolateParams=true",
-		config.Config.MySQLOrchestratorUser,
-		config.Config.MySQLOrchestratorPassword,
-		config.Config.MySQLOrchestratorHost,
-		config.Config.MySQLOrchestratorPort,
+		config.Config.MySQLVTOrcUser,
+		config.Config.MySQLVTOrcPassword,
+		config.Config.MySQLVTOrcHost,
+		config.Config.MySQLVTOrcPort,
 		config.Config.MySQLConnectTimeoutSeconds,
-		config.Config.MySQLOrchestratorReadTimeoutSeconds,
+		config.Config.MySQLVTOrcReadTimeoutSeconds,
 	)
-	if config.Config.MySQLOrchestratorUseMutualTLS {
+	if config.Config.MySQLVTOrcUseMutualTLS {
 		uri, _ = SetupMySQLVTOrcTLS(uri)
 	}
 	return sqlutils.GetDB(uri)
@@ -157,7 +157,7 @@ func OpenVTOrc() (db *sql.DB, err error) {
 			return db, err
 		} else if !fromCache {
 			// first time ever we talk to MySQL
-			query := fmt.Sprintf("create database if not exists %s", config.Config.MySQLOrchestratorDatabase)
+			query := fmt.Sprintf("create database if not exists %s", config.Config.MySQLVTOrcDatabase)
 			if _, err := db.Exec(query); err != nil {
 				log.Errorf(err.Error())
 				return db, err
@@ -166,12 +166,12 @@ func OpenVTOrc() (db *sql.DB, err error) {
 		db, fromCache, err = sqlutils.GetDB(getMySQLURI())
 		if err == nil && !fromCache {
 			// do not show the password but do show what we connect to.
-			safeMySQLURI := fmt.Sprintf("%s:?@tcp(%s:%d)/%s?timeout=%ds", config.Config.MySQLOrchestratorUser,
-				config.Config.MySQLOrchestratorHost, config.Config.MySQLOrchestratorPort, config.Config.MySQLOrchestratorDatabase, config.Config.MySQLConnectTimeoutSeconds)
+			safeMySQLURI := fmt.Sprintf("%s:?@tcp(%s:%d)/%s?timeout=%ds", config.Config.MySQLVTOrcUser,
+				config.Config.MySQLVTOrcHost, config.Config.MySQLVTOrcPort, config.Config.MySQLVTOrcDatabase, config.Config.MySQLConnectTimeoutSeconds)
 			log.Infof("Connected to vtorc backend: %v", safeMySQLURI)
-			if config.Config.MySQLOrchestratorMaxPoolConnections > 0 {
-				log.Infof("VTOrc pool SetMaxOpenConns: %d", config.Config.MySQLOrchestratorMaxPoolConnections)
-				db.SetMaxOpenConns(config.Config.MySQLOrchestratorMaxPoolConnections)
+			if config.Config.MySQLVTOrcMaxPoolConnections > 0 {
+				log.Infof("VTOrc pool SetMaxOpenConns: %d", config.Config.MySQLVTOrcMaxPoolConnections)
+				db.SetMaxOpenConns(config.Config.MySQLVTOrcMaxPoolConnections)
 			}
 			if config.Config.MySQLConnectionLifetimeSeconds > 0 {
 				db.SetConnMaxLifetime(time.Duration(config.Config.MySQLConnectionLifetimeSeconds) * time.Second)
@@ -186,19 +186,19 @@ func OpenVTOrc() (db *sql.DB, err error) {
 		// make the number of backend connections hit the tcp
 		// limit. That's bad.  I could make this setting dynamic
 		// but then people need to know which value to use. For now
-		// allow up to 25% of MySQLOrchestratorMaxPoolConnections
+		// allow up to 25% of MySQLVTOrcMaxPoolConnections
 		// to be idle.  That should provide a good number which
 		// does not keep the maximum number of connections open but
 		// at the same time does not trigger disconnections and
 		// reconnections too frequently.
-		maxIdleConns := int(config.Config.MySQLOrchestratorMaxPoolConnections * 25 / 100)
+		maxIdleConns := int(config.Config.MySQLVTOrcMaxPoolConnections * 25 / 100)
 		if maxIdleConns < 10 {
 			maxIdleConns = 10
 		}
 		log.Infof("Connecting to backend %s:%d: maxConnections: %d, maxIdleConns: %d",
-			config.Config.MySQLOrchestratorHost,
-			config.Config.MySQLOrchestratorPort,
-			config.Config.MySQLOrchestratorMaxPoolConnections,
+			config.Config.MySQLVTOrcHost,
+			config.Config.MySQLVTOrcPort,
+			config.Config.MySQLVTOrcMaxPoolConnections,
 			maxIdleConns)
 		db.SetMaxIdleConns(maxIdleConns)
 	}
diff --git a/go/vt/vtorc/db/tls.go b/go/vt/vtorc/db/tls.go
index d7b28b1b23..c2ffdaddda 100644
--- a/go/vt/vtorc/db/tls.go
+++ b/go/vt/vtorc/db/tls.go
@@ -128,18 +128,18 @@ func SetupMySQLTopologyTLS(uri string) (string, error) {
 // Modify the supplied URI to call the TLS config
 func SetupMySQLVTOrcTLS(uri string) (string, error) {
 	if !vtorcTLSConfigured {
-		tlsConfig, err := ssl.NewTLSConfig(config.Config.MySQLOrchestratorSSLCAFile, !config.Config.MySQLOrchestratorSSLSkipVerify)
+		tlsConfig, err := ssl.NewTLSConfig(config.Config.MySQLVTOrcSSLCAFile, !config.Config.MySQLVTOrcSSLSkipVerify)
 		// Drop to TLS 1.0 for talking to MySQL
 		tlsConfig.MinVersion = tls.VersionTLS10
 		if err != nil {
 			log.Fatalf("Can't create TLS configuration for VTOrc connection %s: %s", uri, err)
 			return "", err
 		}
-		tlsConfig.InsecureSkipVerify = config.Config.MySQLOrchestratorSSLSkipVerify
-		if (!config.Config.MySQLOrchestratorSSLSkipVerify) &&
-			config.Config.MySQLOrchestratorSSLCertFile != "" &&
-			config.Config.MySQLOrchestratorSSLPrivateKeyFile != "" {
-			if err = ssl.AppendKeyPair(tlsConfig, config.Config.MySQLOrchestratorSSLCertFile, config.Config.MySQLOrchestratorSSLPrivateKeyFile); err != nil {
+		tlsConfig.InsecureSkipVerify = config.Config.MySQLVTOrcSSLSkipVerify
+		if (!config.Config.MySQLVTOrcSSLSkipVerify) &&
+			config.Config.MySQLVTOrcSSLCertFile != "" &&
+			config.Config.MySQLVTOrcSSLPrivateKeyFile != "" {
+			if err = ssl.AppendKeyPair(tlsConfig, config.Config.MySQLVTOrcSSLCertFile, config.Config.MySQLVTOrcSSLPrivateKeyFile); err != nil {
 				log.Fatalf("Can't setup TLS key pairs for %s: %s", uri, err)
 				return "", err
 			}
diff --git a/go/vt/vtorc/inst/analysis_dao.go b/go/vt/vtorc/inst/analysis_dao.go
index 887b8c29e3..ab8cc45a5c 100644
--- a/go/vt/vtorc/inst/analysis_dao.go
+++ b/go/vt/vtorc/inst/analysis_dao.go
@@ -54,7 +54,7 @@ func init() {
 func initializeAnalysisDaoPostConfiguration() {
 	config.WaitForConfigurationToBeLoaded()
 
-	recentInstantAnalysis = cache.New(time.Duration(config.RecoveryPollSeconds*2)*time.Second, time.Second)
+	recentInstantAnalysis = cache.New(time.Duration(config.Config.RecoveryPollSeconds*2)*time.Second, time.Second)
 }
 
 type clusterAnalysis struct {
diff --git a/go/vt/vtorc/logic/orchestrator.go b/go/vt/vtorc/logic/orchestrator.go
index d830bc764b..59d038c568 100644
--- a/go/vt/vtorc/logic/orchestrator.go
+++ b/go/vt/vtorc/logic/orchestrator.go
@@ -360,7 +360,7 @@ func ContinuousDiscovery() {
 	healthTick := time.Tick(config.HealthPollSeconds * time.Second)
 	instancePollTick := time.Tick(instancePollSecondsDuration())
 	caretakingTick := time.Tick(time.Minute)
-	recoveryTick := time.Tick(time.Duration(config.RecoveryPollSeconds) * time.Second)
+	recoveryTick := time.Tick(time.Duration(config.Config.RecoveryPollSeconds) * time.Second)
 	tabletTopoTick := OpenTabletDiscovery()
 	var recoveryEntrance int64
 	var snapshotTopologiesTick <-chan time.Time
diff --git a/go/vt/vtorc/logic/tablet_discovery.go b/go/vt/vtorc/logic/tablet_discovery.go
index 56ac7a41e0..d660f87cfe 100644
--- a/go/vt/vtorc/logic/tablet_discovery.go
+++ b/go/vt/vtorc/logic/tablet_discovery.go
@@ -66,8 +66,7 @@ func OpenTabletDiscovery() <-chan time.Time {
 	refreshTabletsUsing(func(instanceKey *inst.InstanceKey) {
 		_ = inst.InjectSeed(instanceKey)
 	}, false /* forceRefresh */)
-	// TODO(sougou): parameterize poll interval.
-	return time.Tick(15 * time.Second) //nolint SA1015: using time.Tick leaks the underlying ticker
+	return time.Tick(time.Second * time.Duration(config.Config.TopoInformationRefreshSeconds)) //nolint SA1015: using time.Tick leaks the underlying ticker
 }
 
 // refreshAllTablets reloads the tablets from topo and discovers the ones which haven't been refreshed in a while
diff --git a/go/vt/vtorc/logic/topology_recovery_dao.go b/go/vt/vtorc/logic/topology_recovery_dao.go
index 52ec30614a..c192a11a97 100644
--- a/go/vt/vtorc/logic/topology_recovery_dao.go
+++ b/go/vt/vtorc/logic/topology_recovery_dao.go
@@ -351,7 +351,7 @@ func ExpireBlockedRecoveries() error {
 				from blocked_topology_recovery
 				where
 					last_blocked_timestamp < NOW() - interval ? second
-			`, (config.RecoveryPollSeconds * 2),
+			`, config.Config.RecoveryPollSeconds*2,
 	)
 	if err != nil {
 		log.Error(err)
diff --git a/go/vt/vtorc/logic/topology_recovery_status.go b/go/vt/vtorc/logic/topology_recovery_status.go
new file mode 100644
index 0000000000..4e85b0529d
--- /dev/null
+++ b/go/vt/vtorc/logic/topology_recovery_status.go
@@ -0,0 +1,48 @@
+/*
+Copyright 2022 The Vitess Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or 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 logic
+
+// TopologyRecoveriesTemplate is the HTML to use to display the
+// topology recovery steps list object
+const TopologyRecoveriesTemplate = `
+<style>
+  table {
+    border-collapse: collapse;
+  }
+  td, th {
+    border: 1px solid #999;
+    padding: 0.2rem;
+  }
+</style>
+<table>
+  <tr>
+    <th colspan="4">Recent Recoveries Performed</th>
+  </tr>
+  <tr>
+    <th>Recovery ID</th>
+    <th>Failure Type</th>
+    <th>Instance</th>
+  </tr>
+  {{range $i, $recovery := .}}
+  <tr>
+    <td>{{$recovery.ID}}</td>
+    <td>{{$recovery.AnalysisEntry.Analysis}}</td>
+    <td>{{$recovery.AnalysisEntry.AnalyzedInstanceKey}}</td>
+  </tr>
+  {{end}}
+</table>
+`
diff --git a/go/vt/vtorc/process/health_dao.go b/go/vt/vtorc/process/health_dao.go
index 8c0bfb2a98..cd5c2b6078 100644
--- a/go/vt/vtorc/process/health_dao.go
+++ b/go/vt/vtorc/process/health_dao.go
@@ -84,8 +84,8 @@ func WriteRegisterNode(nodeHealth *NodeHealth) (healthy bool, err error) {
 		if config.Config.IsSQLite() {
 			dbBackend = config.Config.SQLite3DataFile
 		} else {
-			dbBackend = fmt.Sprintf("%s:%d", config.Config.MySQLOrchestratorHost,
-				config.Config.MySQLOrchestratorPort)
+			dbBackend = fmt.Sprintf("%s:%d", config.Config.MySQLVTOrcHost,
+				config.Config.MySQLVTOrcPort)
 		}
 		sqlResult, err := db.ExecVTOrc(`
 			insert ignore into node_health


Test patch:
diff --git a/.github/docker/cluster_test_12/Dockerfile b/.github/docker/cluster_test_12/Dockerfile
deleted file mode 100644
index 0497e4112d..0000000000
--- a/.github/docker/cluster_test_12/Dockerfile
+++ /dev/null
@@ -1,34 +0,0 @@
-# DO NOT MODIFY: THIS FILE IS GENERATED USING "make generate_ci_workflows"
-
-ARG bootstrap_version=11
-ARG image="vitess/bootstrap:${bootstrap_version}-mysql57"
-
-FROM "${image}"
-
-USER root
-
-# Re-copy sources from working tree
-RUN rm -rf /vt/src/vitess.io/vitess/*
-COPY . /vt/src/vitess.io/vitess
-
-# Set the working directory
-WORKDIR /vt/src/vitess.io/vitess
-
-# Fix permissions
-RUN chown -R vitess:vitess /vt
-
-USER vitess
-
-# Set environment variables
-ENV VTROOT /vt/src/vitess.io/vitess
-# Set the vtdataroot such that it uses the volume mount
-ENV VTDATAROOT /vt/vtdataroot
-
-# create the vtdataroot directory
-RUN mkdir -p $VTDATAROOT
-
-# install goimports
-RUN go install golang.org/x/tools/cmd/goimports@latest
-
-# sleep for 50 minutes
-CMD sleep 3000
diff --git a/.github/docker/cluster_test_18/Dockerfile b/.github/docker/cluster_test_18/Dockerfile
deleted file mode 100644
index 024a441452..0000000000
--- a/.github/docker/cluster_test_18/Dockerfile
+++ /dev/null
@@ -1,37 +0,0 @@
-# DO NOT MODIFY: THIS FILE IS GENERATED USING "make generate_ci_workflows"
-
-ARG bootstrap_version=11
-ARG image="vitess/bootstrap:${bootstrap_version}-mysql57"
-
-FROM "${image}"
-
-USER root
-
-# Re-copy sources from working tree
-RUN rm -rf /vt/src/vitess.io/vitess/*
-COPY . /vt/src/vitess.io/vitess
-
-# Set the working directory
-WORKDIR /vt/src/vitess.io/vitess
-
-# Fix permissions
-RUN chown -R vitess:vitess /vt
-
-USER vitess
-
-# Set environment variables
-ENV VTROOT /vt/src/vitess.io/vitess
-# Set the vtdataroot such that it uses the volume mount
-ENV VTDATAROOT /vt/vtdataroot
-
-# create the vtdataroot directory
-RUN mkdir -p $VTDATAROOT
-
-# install goimports
-RUN go install golang.org/x/tools/cmd/goimports@latest
-
-# make tools
-RUN make tools
-
-# sleep for 50 minutes
-CMD sleep 3000
diff --git a/examples/compose/vtcompose/docker-compose.test.yml b/examples/compose/vtcompose/docker-compose.test.yml
index 3bab7815e2..f05f032a44 100644
--- a/examples/compose/vtcompose/docker-compose.test.yml
+++ b/examples/compose/vtcompose/docker-compose.test.yml
@@ -193,6 +193,7 @@ services:
       - set_keyspace_durability_policy_test_keyspace
       - vtctld
     environment:
+      - WEB_PORT=8080
       - TOPOLOGY_FLAGS=--topo_implementation consul --topo_global_server_address consul1:8500
         --topo_global_root vitess/global
       - EXTERNAL_DB=0
@@ -201,6 +202,7 @@ services:
     image: vitess/lite:${VITESS_TAG:-latest}
     ports:
       - 13000:3000
+      - 13200:8080
     volumes:
       - .:/script
   vttablet101:
diff --git a/go/flags/endtoend/flags_test.go b/go/flags/endtoend/flags_test.go
index cca23c9003..be3db2a8cf 100644
--- a/go/flags/endtoend/flags_test.go
+++ b/go/flags/endtoend/flags_test.go
@@ -47,12 +47,16 @@ var (
 	//go:embed vtctld.txt
 	vtctldTxt string
 
+	//go:embed vtorc.txt
+	vtorcTxt string
+
 	helpOutput = map[string]string{
 		"vtexplain": vtexplainTxt,
 		"vtgate":    vtgateTxt,
 		"vtgr":      vtgrTxt,
 		"vttablet":  vttabletTxt,
 		"vtctld":    vtctldTxt,
+		"vtorc":     vtorcTxt,
 	}
 )
 
diff --git a/go/test/endtoend/cluster/vtorc_process.go b/go/test/endtoend/cluster/vtorc_process.go
index c587632d4c..d8563243a6 100644
--- a/go/test/endtoend/cluster/vtorc_process.go
+++ b/go/test/endtoend/cluster/vtorc_process.go
@@ -98,8 +98,8 @@ func (orc *VTOrcProcess) Setup() (err error) {
 	}
 
 	/* minimal command line arguments:
-	$ vtorc -topo_implementation etcd2 -topo_global_server_address localhost:2379 -topo_global_root /vitess/global
-	-config config/vtorc/default.json -alsologtostderr http
+	$ vtorc --topo_implementation etcd2 --topo_global_server_address localhost:2379 --topo_global_root /vitess/global
+	--config config/vtorc/default.json --alsologtostderr
 	*/
 	orc.proc = exec.Command(
 		orc.Binary,
@@ -114,14 +114,14 @@ func (orc *VTOrcProcess) Setup() (err error) {
 	}
 
 	orc.proc.Args = append(orc.proc.Args, orc.ExtraArgs...)
-	orc.proc.Args = append(orc.proc.Args, "--alsologtostderr", "http")
+	orc.proc.Args = append(orc.proc.Args, "--alsologtostderr")
 
 	errFile, _ := os.Create(path.Join(orc.LogDir, fmt.Sprintf("orc-stderr-%d.txt", timeNow)))
 	orc.proc.Stderr = errFile
 
 	orc.proc.Env = append(orc.proc.Env, os.Environ()...)
 
-	log.Infof("Running orc with command: %v", strings.Join(orc.proc.Args, " "))
+	log.Infof("Running vtorc with command: %v", strings.Join(orc.proc.Args, " "))
 
 	err = orc.proc.Start()
 	if err != nil {
diff --git a/go/vt/vtctl/reparentutil/planned_reparenter_flaky_test.go b/go/vt/vtctl/reparentutil/planned_reparenter_flaky_test.go
index 09900c4a62..5c79caeadb 100644
--- a/go/vt/vtctl/reparentutil/planned_reparenter_flaky_test.go
+++ b/go/vt/vtctl/reparentutil/planned_reparenter_flaky_test.go
@@ -635,6 +635,7 @@ func TestPlannedReparenter_preflightChecks(t *testing.T) {
 					Cell: "zone1",
 					Uid:  500,
 				},
+				durability: &durabilityNone{},
 			},
 			expectedIsNoop: false,
 			expectedEvent: &events.Reparent{
@@ -662,6 +663,7 @@ func TestPlannedReparenter_preflightChecks(t *testing.T) {
 					Cell: "zone1",
 					Uid:  100,
 				},
+				durability: &durabilityNone{},
 			},
 			shouldErr: false,
 		},
@@ -827,6 +829,81 @@ func TestPlannedReparenter_preflightChecks(t *testing.T) {
 			},
 			shouldErr: false,
 		},
+		{
+			name: "primary elect can't make forward progress",
+			tmc: &testutil.TabletManagerClient{
+				ReplicationStatusResults: map[string]struct {
+					Position *replicationdatapb.Status
+					Error    error
+				}{
+					"zone1-0000000100": { // most advanced position
+						Position: &replicationdatapb.Status{
+							Position: "MySQL56/3E11FA47-71CA-11E1-9E33-C80AA9429562:1-10",
+						},
+					},
+					"zone1-0000000101": {
+						Position: &replicationdatapb.Status{
+							Position: "MySQL56/3E11FA47-71CA-11E1-9E33-C80AA9429562:1-5",
+						},
+					},
+				},
+			},
+			ev: &events.Reparent{
+				ShardInfo: *topo.NewShardInfo("testkeyspace", "-", &topodatapb.Shard{
+					PrimaryAlias: &topodatapb.TabletAlias{
+						Cell: "zone1",
+						Uid:  500,
+					},
+				}, nil),
+			},
+			tabletMap: map[string]*topo.TabletInfo{
+				"zone1-0000000100": {
+					Tablet: &topodatapb.Tablet{
+						Alias: &topodatapb.TabletAlias{
+							Cell: "zone1",
+							Uid:  100,
+						},
+						Type: topodatapb.TabletType_REPLICA,
+					},
+				},
+				"zone1-0000000101": {
+					Tablet: &topodatapb.Tablet{
+						Alias: &topodatapb.TabletAlias{
+							Cell: "zone1",
+							Uid:  101,
+						},
+						Type: topodatapb.TabletType_REPLICA,
+					},
+				},
+				"zone1-0000000500": {
+					Tablet: &topodatapb.Tablet{
+						Alias: &topodatapb.TabletAlias{
+							Cell: "zone1",
+							Uid:  500,
+						},
+						Type: topodatapb.TabletType_PRIMARY,
+					},
+				},
+			},
+			opts: &PlannedReparentOptions{
+				// Avoid the current primary.
+				AvoidPrimaryAlias: &topodatapb.TabletAlias{
+					Cell: "zone1",
+					Uid:  500,
+				},
+				durability: &durabilityCrossCell{},
+			},
+			expectedIsNoop: true,
+			expectedEvent: &events.Reparent{
+				ShardInfo: *topo.NewShardInfo("testkeyspace", "-", &topodatapb.Shard{
+					PrimaryAlias: &topodatapb.TabletAlias{
+						Cell: "zone1",
+						Uid:  500,
+					},
+				}, nil),
+			},
+			shouldErr: true,
+		},
 	}
 
 	ctx := context.Background()
@@ -849,6 +926,11 @@ func TestPlannedReparenter_preflightChecks(t *testing.T) {
 			}()
 
 			pr := NewPlannedReparenter(tt.ts, tt.tmc, logger)
+			if tt.opts.durability == nil {
+				durability, err := GetDurabilityPolicy("none")
+				require.NoError(t, err)
+				tt.opts.durability = durability
+			}
 			isNoop, err := pr.preflightChecks(ctx, tt.ev, tt.keyspace, tt.shard, tt.tabletMap, tt.opts)
 			if tt.shouldErr {
 				assert.Error(t, err)
diff --git a/go/vt/vtgate/planbuilder/rewrite_test.go b/go/vt/vtgate/planbuilder/rewrite_test.go
index d4815deebb..b2e9fc7683 100644
--- a/go/vt/vtgate/planbuilder/rewrite_test.go
+++ b/go/vt/vtgate/planbuilder/rewrite_test.go
@@ -120,10 +120,10 @@ func TestHavingRewrite(t *testing.T) {
 		output: "select 1 from t1 where (x = 1 or y = 2) and a = 1 having count(*) = 1",
 	}, {
 		input:  "select count(*) k from t1 where x = 1 or y = 2 having a = 1 and k = 1",
-		output: "select count(*) as k from t1 where (x = 1 or y = 2) and a = 1 having k = 1",
+		output: "select count(*) as k from t1 where (x = 1 or y = 2) and a = 1 having count(*) = 1",
 	}, {
 		input:  "select count(*) k from t1 having k = 10",
-		output: "select count(*) as k from t1 having k = 10",
+		output: "select count(*) as k from t1 having count(*) = 10",
 	}, {
 		input:  "select 1 from t1 where x in (select 1 from t2 having a = 1)",
 		output: "select 1 from t1 where :__sq_has_values1 = 1 and x in ::__sq1",
diff --git a/go/vt/vtgate/planbuilder/testdata/aggr_cases.txt b/go/vt/vtgate/planbuilder/testdata/aggr_cases.txt
index 49042c1056..15d7a04d50 100644
--- a/go/vt/vtgate/planbuilder/testdata/aggr_cases.txt
+++ b/go/vt/vtgate/planbuilder/testdata/aggr_cases.txt
@@ -1073,7 +1073,7 @@ Gen4 error: unsupported: '*' expression in cross-shard query
       "Sharded": true
     },
     "FieldQuery": "select id, count(*) as c from `user` where 1 != 1 group by id",
-    "Query": "select id, count(*) as c from `user` where id = 1 group by id having c = 10",
+    "Query": "select id, count(*) as c from `user` where id = 1 group by id having count(*) = 10",
     "Table": "`user`",
     "Values": [
       "INT64(1)"
@@ -3388,7 +3388,7 @@ Gen4 plan same as above
   "Original": "select count(*) a from user having a = 10",
   "Instructions": {
     "OperatorType": "Filter",
-    "Predicate": "a = 10",
+    "Predicate": ":0 = 10",
     "Inputs": [
       {
         "OperatorType": "Aggregate",
@@ -3423,7 +3423,7 @@ Gen4 plan same as above
   "Original": "select count(*) a from user having a = '1'",
   "Instructions": {
     "OperatorType": "Filter",
-    "Predicate": "a = '1'",
+    "Predicate": ":0 = '1'",
     "Inputs": [
       {
         "OperatorType": "Aggregate",
@@ -3458,7 +3458,7 @@ Gen4 plan same as above
   "Original": "select count(*) a from user having a != 10",
   "Instructions": {
     "OperatorType": "Filter",
-    "Predicate": "a != 10",
+    "Predicate": ":0 != 10",
     "Inputs": [
       {
         "OperatorType": "Aggregate",
@@ -3493,7 +3493,7 @@ Gen4 plan same as above
   "Original": "select count(*) a from user having a != '1'",
   "Instructions": {
     "OperatorType": "Filter",
-    "Predicate": "a != '1'",
+    "Predicate": ":0 != '1'",
     "Inputs": [
       {
         "OperatorType": "Aggregate",
@@ -3528,7 +3528,7 @@ Gen4 plan same as above
   "Original": "select count(*) a from user having a \u003e 10",
   "Instructions": {
     "OperatorType": "Filter",
-    "Predicate": "a \u003e 10",
+    "Predicate": ":0 \u003e 10",
     "Inputs": [
       {
         "OperatorType": "Aggregate",
@@ -3563,7 +3563,7 @@ Gen4 plan same as above
   "Original": "select count(*) a from user having a \u003e= 10",
   "Instructions": {
     "OperatorType": "Filter",
-    "Predicate": "a \u003e= 10",
+    "Predicate": ":0 \u003e= 10",
     "Inputs": [
       {
         "OperatorType": "Aggregate",
@@ -3598,7 +3598,7 @@ Gen4 plan same as above
   "Original": "select count(*) a from user having a \u003c 10",
   "Instructions": {
     "OperatorType": "Filter",
-    "Predicate": "a \u003c 10",
+    "Predicate": ":0 \u003c 10",
     "Inputs": [
       {
         "OperatorType": "Aggregate",
@@ -3633,7 +3633,7 @@ Gen4 plan same as above
   "Original": "select count(*) a from user having a \u003c= 10",
   "Instructions": {
     "OperatorType": "Filter",
-    "Predicate": "a \u003c= 10",
+    "Predicate": ":0 \u003c= 10",
     "Inputs": [
       {
         "OperatorType": "Aggregate",
@@ -3668,7 +3668,7 @@ Gen4 plan same as above
   "Original": "select col, count(*) a from user group by col having a \u003c= 10",
   "Instructions": {
     "OperatorType": "Filter",
-    "Predicate": "a \u003c= 10",
+    "Predicate": ":1 \u003c= 10",
     "Inputs": [
       {
         "OperatorType": "Aggregate",
@@ -3712,7 +3712,7 @@ Gen4 plan same as above
     "Inputs": [
       {
         "OperatorType": "Filter",
-        "Predicate": "a = 1.00",
+        "Predicate": ":0 = 1.00",
         "Inputs": [
           {
             "OperatorType": "Aggregate",
@@ -4913,7 +4913,7 @@ Gen4 error: aggregate functions take a single argument 'count(distinct user_id,
     "Inputs": [
       {
         "OperatorType": "Filter",
-        "Predicate": "fooSum + :2 = 42",
+        "Predicate": ":1 + :2 = 42",
         "Inputs": [
           {
             "OperatorType": "Aggregate",
diff --git a/go/vt/vtgate/planbuilder/testdata/postprocess_cases.txt b/go/vt/vtgate/planbuilder/testdata/postprocess_cases.txt
index a18d6c4e53..9e96746f5b 100644
--- a/go/vt/vtgate/planbuilder/testdata/postprocess_cases.txt
+++ b/go/vt/vtgate/planbuilder/testdata/postprocess_cases.txt
@@ -3027,7 +3027,7 @@ Gen4 plan same as above
   "Original": "select count(*) a from user having a = 0x01",
   "Instructions": {
     "OperatorType": "Filter",
-    "Predicate": "a = 0x01",
+    "Predicate": ":0 = 0x01",
     "Inputs": [
       {
         "OperatorType": "Aggregate",
diff --git a/go/vt/vtgate/planbuilder/testdata/select_cases.txt b/go/vt/vtgate/planbuilder/testdata/select_cases.txt
index 635d6a89e4..bebdde5382 100644
--- a/go/vt/vtgate/planbuilder/testdata/select_cases.txt
+++ b/go/vt/vtgate/planbuilder/testdata/select_cases.txt
@@ -4366,7 +4366,7 @@ Gen4 plan same as above
       "Sharded": false
     },
     "FieldQuery": "select 1 from (select col, count(*) as a from unsharded where 1 != 1 group by col) as f left join unsharded as u on f.col = u.id where 1 != 1",
-    "Query": "select 1 from (select col, count(*) as a from unsharded group by col having a \u003e 0 limit 0, 12) as f left join unsharded as u on f.col = u.id",
+    "Query": "select 1 from (select col, count(*) as a from unsharded group by col having count(*) \u003e 0 limit 0, 12) as f left join unsharded as u on f.col = u.id",
     "Table": "unsharded"
   },
   "TablesUsed": [
diff --git a/go/vt/vtgate/semantics/analyzer_test.go b/go/vt/vtgate/semantics/analyzer_test.go
index cfb7d0d229..f7bfe3b04f 100644
--- a/go/vt/vtgate/semantics/analyzer_test.go
+++ b/go/vt/vtgate/semantics/analyzer_test.go
@@ -812,6 +812,9 @@ func TestHavingBinding(t *testing.T) {
 	}, {
 		"select t.id, count(*) as a from t, t1 group by t.id having a = 1",
 		MergeTableSets(T1, T2),
+	}, {
+		"select t.id, sum(t2.name) as a from t, t2 group by t.id having a = 1",
+		T2,
 	}, {
 		sql:  "select u2.a, u1.a from u1, u2 having u2.a = 2",
 		deps: T2,
diff --git a/go/vt/vtgate/semantics/early_rewriter_test.go b/go/vt/vtgate/semantics/early_rewriter_test.go
index bb9642a57e..9b40227af9 100644
--- a/go/vt/vtgate/semantics/early_rewriter_test.go
+++ b/go/vt/vtgate/semantics/early_rewriter_test.go
@@ -145,7 +145,7 @@ func TestExpandStar(t *testing.T) {
 		expSQL: "select t1.b as b, t1.a as a, t1.c as c, t5.a as a from t1 join t5 where t1.b = t5.b",
 	}, {
 		sql:    "select * from t1 join t5 using (b) having b = 12",
-		expSQL: "select t1.b as b, t1.a as a, t1.c as c, t5.a as a from t1 join t5 where t1.b = t5.b having b = 12",
+		expSQL: "select t1.b as b, t1.a as a, t1.c as c, t5.a as a from t1 join t5 where t1.b = t5.b having t1.b = 12",
 	}, {
 		sql:    "select 1 from t1 join t5 using (b) having b = 12",
 		expSQL: "select 1 from t1 join t5 where t1.b = t5.b having t1.b = 12",
@@ -305,6 +305,35 @@ func TestOrderByGroupByLiteral(t *testing.T) {
 	}
 }
 
+func TestHavingByColumnName(t *testing.T) {
+	schemaInfo := &FakeSI{
+		Tables: map[string]*vindexes.Table{},
+	}
+	cDB := "db"
+	tcases := []struct {
+		sql    string
+		expSQL string
+		expErr string
+	}{{
+		sql:    "select id, sum(foo) as sumOfFoo from t1 having sumOfFoo > 1",
+		expSQL: "select id, sum(foo) as sumOfFoo from t1 having sum(foo) > 1",
+	}}
+	for _, tcase := range tcases {
+		t.Run(tcase.sql, func(t *testing.T) {
+			ast, err := sqlparser.Parse(tcase.sql)
+			require.NoError(t, err)
+			selectStatement := ast.(*sqlparser.Select)
+			_, err = Analyze(selectStatement, cDB, schemaInfo)
+			if tcase.expErr == "" {
+				require.NoError(t, err)
+				assert.Equal(t, tcase.expSQL, sqlparser.String(selectStatement))
+			} else {
+				require.EqualError(t, err, tcase.expErr)
+			}
+		})
+	}
+}
+
 func TestSemTableDependenciesAfterExpandStar(t *testing.T) {
 	schemaInfo := &FakeSI{Tables: map[string]*vindexes.Table{
 		"t1": {
diff --git a/go/vt/vtorc/config/config_test.go b/go/vt/vtorc/config/config_test.go
index 102e770790..b1a7258716 100644
--- a/go/vt/vtorc/config/config_test.go
+++ b/go/vt/vtorc/config/config_test.go
@@ -3,7 +3,7 @@ package config
 import (
 	"testing"
 
-	test "vitess.io/vitess/go/vt/vtorc/external/golib/tests"
+	"github.com/stretchr/testify/require"
 )
 
 func init() {
@@ -16,32 +16,32 @@ func TestRecoveryPeriodBlock(t *testing.T) {
 		c.RecoveryPeriodBlockSeconds = 0
 		c.RecoveryPeriodBlockMinutes = 0
 		err := c.postReadAdjustments()
-		test.S(t).ExpectNil(err)
-		test.S(t).ExpectEquals(c.RecoveryPeriodBlockSeconds, 0)
+		require.NoError(t, err)
+		require.EqualValues(t, 0, c.RecoveryPeriodBlockSeconds)
 	}
 	{
 		c := newConfiguration()
 		c.RecoveryPeriodBlockSeconds = 30
 		c.RecoveryPeriodBlockMinutes = 1
 		err := c.postReadAdjustments()
-		test.S(t).ExpectNil(err)
-		test.S(t).ExpectEquals(c.RecoveryPeriodBlockSeconds, 30)
+		require.NoError(t, err)
+		require.EqualValues(t, 30, c.RecoveryPeriodBlockSeconds)
 	}
 	{
 		c := newConfiguration()
 		c.RecoveryPeriodBlockSeconds = 0
 		c.RecoveryPeriodBlockMinutes = 2
 		err := c.postReadAdjustments()
-		test.S(t).ExpectNil(err)
-		test.S(t).ExpectEquals(c.RecoveryPeriodBlockSeconds, 120)
+		require.NoError(t, err)
+		require.EqualValues(t, 120, c.RecoveryPeriodBlockSeconds)
 	}
 	{
 		c := newConfiguration()
 		c.RecoveryPeriodBlockSeconds = 15
 		c.RecoveryPeriodBlockMinutes = 0
 		err := c.postReadAdjustments()
-		test.S(t).ExpectNil(err)
-		test.S(t).ExpectEquals(c.RecoveryPeriodBlockSeconds, 15)
+		require.NoError(t, err)
+		require.EqualValues(t, 15, c.RecoveryPeriodBlockSeconds)
 	}
 }
 
@@ -51,21 +51,21 @@ func TestRaft(t *testing.T) {
 		c.RaftBind = "1.2.3.4:1008"
 		c.RaftDataDir = "/path/to/somewhere"
 		err := c.postReadAdjustments()
-		test.S(t).ExpectNil(err)
-		test.S(t).ExpectEquals(c.RaftAdvertise, c.RaftBind)
+		require.NoError(t, err)
+		require.EqualValues(t, c.RaftAdvertise, c.RaftBind)
 	}
 	{
 		c := newConfiguration()
 		c.RaftEnabled = true
 		err := c.postReadAdjustments()
-		test.S(t).ExpectNotNil(err)
+		require.Error(t, err)
 	}
 	{
 		c := newConfiguration()
 		c.RaftEnabled = true
 		c.RaftDataDir = "/path/to/somewhere"
 		err := c.postReadAdjustments()
-		test.S(t).ExpectNil(err)
+		require.NoError(t, err)
 	}
 	{
 		c := newConfiguration()
@@ -73,7 +73,7 @@ func TestRaft(t *testing.T) {
 		c.RaftDataDir = "/path/to/somewhere"
 		c.RaftBind = ""
 		err := c.postReadAdjustments()
-		test.S(t).ExpectNotNil(err)
+		require.Error(t, err)
 	}
 }
 
@@ -82,30 +82,30 @@ func TestHttpAdvertise(t *testing.T) {
 		c := newConfiguration()
 		c.HTTPAdvertise = ""
 		err := c.postReadAdjustments()
-		test.S(t).ExpectNil(err)
+		require.NoError(t, err)
 	}
 	{
 		c := newConfiguration()
 		c.HTTPAdvertise = "http://127.0.0.1:1234"
 		err := c.postReadAdjustments()
-		test.S(t).ExpectNil(err)
+		require.NoError(t, err)
 	}
 	{
 		c := newConfiguration()
 		c.HTTPAdvertise = "http://127.0.0.1"
 		err := c.postReadAdjustments()
-		test.S(t).ExpectNotNil(err)
+		require.Error(t, err)
 	}
 	{
 		c := newConfiguration()
 		c.HTTPAdvertise = "127.0.0.1:1234"
 		err := c.postReadAdjustments()
-		test.S(t).ExpectNotNil(err)
+		require.Error(t, err)
 	}
 	{
 		c := newConfiguration()
 		c.HTTPAdvertise = "http://127.0.0.1:1234/mypath"
 		err := c.postReadAdjustments()
-		test.S(t).ExpectNotNil(err)
+		require.Error(t, err)
 	}
 }
diff --git a/test/ci_workflow_gen.go b/test/ci_workflow_gen.go
index 06e978a81b..23e43fa2da 100644
--- a/test/ci_workflow_gen.go
+++ b/test/ci_workflow_gen.go
@@ -69,11 +69,13 @@ var (
 	// Hence, they are not listed in the list below.
 	clusterList = []string{
 		"vtctlbackup_sharded_clustertest_heavy",
+		"12",
 		"13",
 		"ers_prs_newfeatures_heavy",
 		"15",
 		"vtgate_general_heavy",
 		"vtbackup_transform",
+		"18",
 		"xb_backup",
 		"21",
 		"22",
@@ -127,8 +129,6 @@ var (
 	}
 
 	clusterSelfHostedList = []string{
-		"12",
-		"18",
 		"vtorc",
 	}
 	clusterDockerList           = []string{}