Repository: etcd-io/etcd

Issue:
Inconsistent documentation in compaction period comments
### Bug report criteria

- [X] This bug report is not security related, security issues should be disclosed privately via [etcd maintainers](mailto:etcd-maintainers@googlegroups.com).
- [X] This is not a support request or question, support requests or questions should be raised in the etcd [discussion forums](https://github.com/etcd-io/etcd/discussions).
- [X] You have read the etcd [bug reporting guidelines](https://github.com/etcd-io/etcd/blob/main/Documentation/contributor-guide/reporting_bugs.md).
- [X] Existing open issues along with etcd [frequently asked questions](https://etcd.io/docs/latest/faq) have been checked and this is not a duplicate.

### What happened?

There appears to be an inconsistency in the comments describing the compaction period logic. In the current implementation, the comments show conflicting time periods:

Current comments:
```go
Compaction period 24-hour:
  1. compute compaction period, which is 1-hour      // Inconsistent with 24-hour period
  2. record revisions for every 1/10 of 1-hour (6-minute)  // Should be 1/10 of 24-hour (144-minute)
  3. keep recording revisions with no compaction for first 24-hour
  4. do compact with revs[0]
    - success? continue on for-loop and move sliding window; revs = revs[1:]
    - failure? update revs, and retry after 1/10 of 1-hour (6-minute)  // Should be 144-minute

### What did you expect to happen?

Compaction period 24-hour:
  1. compute compaction period, which is 24-hour
  2. record revisions for every 1/10 of 24-hour (144-minute)
  3. keep recording revisions with no compaction for first 24-hour
  4. do compact with revs[0]
    - success? continue on for-loop and move sliding window; revs = revs[1:]
    - failure? update revs, and retry after 1/10 of 24-hour (144-minute)

### How can we reproduce it (as minimally and precisely as possible)?

server/etcdserver/api/v3compactor/periodic.go

### Anything else we need to know?

_No response_

### Etcd version (please run commands below)

<details>

```console
$ etcd --version
latest

$ etcdctl version
# paste output here
```

</details>


### Etcd configuration (command line flags or environment variables)

<details>

# paste your configuration here

</details>


### Etcd debug information (please run commands below, feel free to obfuscate the IP address or FQDN in the output)

<details>

```console
$ etcdctl member list -w table
# paste output here

$ etcdctl --endpoints=<member list> endpoint status -w table
# paste output here
```

</details>


### Relevant log output

_No response_

Discussion:
@stale: This issue has been automatically marked as stale because it has not had recent activity. It will be closed after 21 days if no further activity occurs. Thank you for your contributions.

Solution patch:
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index a270f436a..b040a09c9 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -40,7 +40,7 @@ jobs:
         uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
       # Initializes the CodeQL tools for scanning.
       - name: Initialize CodeQL
-        uses: github/codeql-action/init@48ab28a6f5dbc2a99bf1e0131198dd8f1df78169 # v3.28.0
+        uses: github/codeql-action/init@f6091c0113d1dcf9b98e269ee48e8a7e51b7bdd4 # v3.28.5
         with:
           # If you wish to specify custom queries, you can do so here or in a config file.
           # By default, queries listed here will override any specified in a config file.
@@ -50,6 +50,6 @@ jobs:
       # Autobuild attempts to build any compiled languages  (C/C++, C#, or Java).
       # If this step fails, then you should remove it and run the build manually (see below)
       - name: Autobuild
-        uses: github/codeql-action/autobuild@48ab28a6f5dbc2a99bf1e0131198dd8f1df78169 # v3.28.0
+        uses: github/codeql-action/autobuild@f6091c0113d1dcf9b98e269ee48e8a7e51b7bdd4 # v3.28.5
       - name: Perform CodeQL Analysis
-        uses: github/codeql-action/analyze@48ab28a6f5dbc2a99bf1e0131198dd8f1df78169 # v3.28.0
+        uses: github/codeql-action/analyze@f6091c0113d1dcf9b98e269ee48e8a7e51b7bdd4 # v3.28.5
diff --git a/.github/workflows/contrib.yaml b/.github/workflows/contrib.yaml
index fc3f040c6..835d7b256 100644
--- a/.github/workflows/contrib.yaml
+++ b/.github/workflows/contrib.yaml
@@ -9,7 +9,7 @@ jobs:
       - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
       - id: goversion
         run: echo "goversion=$(cat .go-version)" >> "$GITHUB_OUTPUT"
-      - uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
+      - uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0
         with:
           go-version: ${{ steps.goversion.outputs.goversion }}
       - run: |
diff --git a/.github/workflows/coverage.yaml b/.github/workflows/coverage.yaml
index 69ec34868..9f30c0c81 100644
--- a/.github/workflows/coverage.yaml
+++ b/.github/workflows/coverage.yaml
@@ -16,7 +16,7 @@ jobs:
       - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
       - id: goversion
         run: echo "goversion=$(cat .go-version)" >> "$GITHUB_OUTPUT"
-      - uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
+      - uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0
         with:
           go-version: ${{ steps.goversion.outputs.goversion }}
       - env:
diff --git a/.github/workflows/fuzzing.yaml b/.github/workflows/fuzzing.yaml
index 8ab8c374a..52ae502e5 100644
--- a/.github/workflows/fuzzing.yaml
+++ b/.github/workflows/fuzzing.yaml
@@ -13,14 +13,14 @@ jobs:
       - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
       - id: goversion
         run: echo "goversion=$(cat .go-version)" >> "$GITHUB_OUTPUT"
-      - uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
+      - uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0
         with:
           go-version: ${{ steps.goversion.outputs.goversion }}
       - run: |
           set -euo pipefail
 
           GOARCH=amd64 CPU=4 make fuzz
-      - uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
+      - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
         if: failure()
         with:
           path: "${{env.TARGET_PATH}}/testdata/fuzz/**/*"
diff --git a/.github/workflows/grpcproxy.yaml b/.github/workflows/grpcproxy.yaml
index e15a37bc3..93e6a20bd 100644
--- a/.github/workflows/grpcproxy.yaml
+++ b/.github/workflows/grpcproxy.yaml
@@ -15,7 +15,7 @@ jobs:
       - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
       - id: goversion
         run: echo "goversion=$(cat .go-version)" >> "$GITHUB_OUTPUT"
-      - uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
+      - uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0
         with:
           go-version: ${{ steps.goversion.outputs.goversion }}
       - env:
diff --git a/.github/workflows/measure-testgrid-flakiness.yaml b/.github/workflows/measure-testgrid-flakiness.yaml
index 97ecf0c95..67bdd68d3 100644
--- a/.github/workflows/measure-testgrid-flakiness.yaml
+++ b/.github/workflows/measure-testgrid-flakiness.yaml
@@ -15,7 +15,7 @@ jobs:
       - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
       - id: goversion
         run: echo "goversion=$(cat .go-version)" >> "$GITHUB_OUTPUT"
-      - uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
+      - uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0
         with:
           go-version: ${{ steps.goversion.outputs.goversion }}
       - env:
diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml
index 3f370fb57..0802972f3 100644
--- a/.github/workflows/release.yaml
+++ b/.github/workflows/release.yaml
@@ -9,7 +9,7 @@ jobs:
       - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
       - id: goversion
         run: echo "goversion=$(cat .go-version)" >> "$GITHUB_OUTPUT"
-      - uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
+      - uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0
         with:
           go-version: ${{ steps.goversion.outputs.goversion }}
       - name: release
@@ -36,7 +36,7 @@ jobs:
         run: |
           docker image save -o /tmp/etcd-img.tar gcr.io/etcd-development/etcd
       - name: upload-image
-        uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
+        uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
         with:
           name: etcd-img
           path: /tmp/etcd-img.tar
@@ -72,6 +72,6 @@ jobs:
           # Ref: https://github.com/aquasecurity/trivy-action/issues/389
           TRIVY_DB_REPOSITORY: 'public.ecr.aws/aquasecurity/trivy-db:2'
       - name: upload scan results
-        uses: github/codeql-action/upload-sarif@48ab28a6f5dbc2a99bf1e0131198dd8f1df78169 # v3.28.0
+        uses: github/codeql-action/upload-sarif@f6091c0113d1dcf9b98e269ee48e8a7e51b7bdd4 # v3.28.5
         with:
           sarif_file: 'trivy-results-${{ matrix.platforms }}.sarif'
diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml
index 439b4faf9..46fd4ecf0 100644
--- a/.github/workflows/scorecards.yml
+++ b/.github/workflows/scorecards.yml
@@ -42,7 +42,7 @@ jobs:
       # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
       # format to the repository Actions tab.
       - name: "Upload artifact"
-        uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
+        uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
         with:
           name: SARIF file
           path: results.sarif
@@ -50,6 +50,6 @@ jobs:
 
       # Upload the results to GitHub's code scanning dashboard.
       - name: "Upload to code-scanning"
-        uses: github/codeql-action/upload-sarif@48ab28a6f5dbc2a99bf1e0131198dd8f1df78169 # v3.28.0
+        uses: github/codeql-action/upload-sarif@f6091c0113d1dcf9b98e269ee48e8a7e51b7bdd4 # v3.28.5
         with:
           sarif_file: results.sarif
diff --git a/CHANGELOG/CHANGELOG-3.4.md b/CHANGELOG/CHANGELOG-3.4.md
index 2fb76bdb3..ee72f64f6 100644
--- a/CHANGELOG/CHANGELOG-3.4.md
+++ b/CHANGELOG/CHANGELOG-3.4.md
@@ -8,6 +8,7 @@ Previous change logs can be found at [CHANGELOG-3.3](https://github.com/etcd-io/
 
 ### etcd server
 - [Avoid deadlock in etcd.Close when stopping during bootstrapping](https://github.com/etcd-io/etcd/pull/19166)
+- Fix [missing delete event on watch opened on same revision as compaction request](https://github.com/etcd-io/etcd/pull/19251)
 
 ### Package `clientv3`
 - Fix [runtime panic that occurs when KeepAlive is called with a Context implemented by an uncomparable type](https://github.com/etcd-io/etcd/pull/18936)
diff --git a/CHANGELOG/CHANGELOG-3.5.md b/CHANGELOG/CHANGELOG-3.5.md
index 34e9bba50..4bdfe949e 100644
--- a/CHANGELOG/CHANGELOG-3.5.md
+++ b/CHANGELOG/CHANGELOG-3.5.md
@@ -3,11 +3,19 @@
 Previous change logs can be found at [CHANGELOG-3.4](https://github.com/etcd-io/etcd/blob/main/CHANGELOG/CHANGELOG-3.4.md).
 <hr>
 
-## v3.5.18 (TBC)
+## v3.5.19 (TBC)
+
+### `tools/benchmark`
+- Backport [add mixed read-write performance evaluation scripts](https://github.com/etcd-io/etcd/pull/19275).
+
+<hr>
+
+## v3.5.18 (2025-01-24)
 
 ### etcd server
-- [Avoid deadlock in etcd.Close when stopping during bootstrapping](https://github.com/etcd-io/etcd/pull/19167)
+- Avoid deadlock in etcd.Close when stopping during bootstrapping, see https://github.com/etcd-io/etcd/pull/19167 and https://github.com/etcd-io/etcd/pull/19258.
 - [Print warning messages if any of the deprecated v2store related flags is set](https://github.com/etcd-io/etcd/pull/18999)
+- Fix [missing delete event on watch opened on same revision as compaction request](https://github.com/etcd-io/etcd/pull/19249)
 
 ### Package `clientv3`
 - Fix [runtime panic that occurs when KeepAlive is called with a Context implemented by an uncomparable type](https://github.com/etcd-io/etcd/pull/18937)
@@ -21,6 +29,8 @@ Previous change logs can be found at [CHANGELOG-3.4](https://github.com/etcd-io/
 ### Dependencies
 - Bump [golang-jwt/jwt to 4.5.1 to address GO-2024-3250](https://github.com/etcd-io/etcd/pull/18899).
 - Compile binaries using [go 1.22.11](https://github.com/etcd-io/etcd/pull/19211).
+- Bump [golang.org/x/crypto to 0.32.0 to address CVE-2024-45337](https://github.com/etcd-io/etcd/pull/19154).
+- Bump [golang.org/x/net to 0.34.0 to address CVE-2024-45338](https://github.com/etcd-io/etcd/pull/19158).
 
 <hr>
 
diff --git a/CHANGELOG/CHANGELOG-3.6.md b/CHANGELOG/CHANGELOG-3.6.md
index c6569365d..99fe5b5ab 100644
--- a/CHANGELOG/CHANGELOG-3.6.md
+++ b/CHANGELOG/CHANGELOG-3.6.md
@@ -107,5 +107,6 @@ See [List of metrics](https://etcd.io/docs/latest/metrics/) for all metrics per
 
 - Use Distroless as base image to make the image less vulnerable and reduce image size.
 - [Upgrade grpc-gateway from v1 to v2](https://github.com/etcd-io/etcd/pull/16595).
+- [Switch from grpc-ecosystem/go-grpc-prometheus to grpc-ecosystem/go-grpc-middleware/providers/prometheus](https://github.com/etcd-io/etcd/pull/19195).
 
 <hr>
diff --git a/api/etcdserverpb/raft_internal.pb.go b/api/etcdserverpb/raft_internal.pb.go
index d39efef7c..e1cd9ebec 100644
--- a/api/etcdserverpb/raft_internal.pb.go
+++ b/api/etcdserverpb/raft_internal.pb.go
@@ -105,6 +105,7 @@ type InternalRaftRequest struct {
 	ClusterVersionSet        *membershippb.ClusterVersionSetRequest    `protobuf:"bytes,1300,opt,name=cluster_version_set,json=clusterVersionSet,proto3" json:"cluster_version_set,omitempty"`
 	ClusterMemberAttrSet     *membershippb.ClusterMemberAttrSetRequest `protobuf:"bytes,1301,opt,name=cluster_member_attr_set,json=clusterMemberAttrSet,proto3" json:"cluster_member_attr_set,omitempty"`
 	DowngradeInfoSet         *membershippb.DowngradeInfoSetRequest     `protobuf:"bytes,1302,opt,name=downgrade_info_set,json=downgradeInfoSet,proto3" json:"downgrade_info_set,omitempty"`
+	DowngradeVersionTest     *DowngradeVersionTestRequest              `protobuf:"bytes,9900,opt,name=downgrade_version_test,json=downgradeVersionTest,proto3" json:"downgrade_version_test,omitempty"`
 	XXX_NoUnkeyedLiteral     struct{}                                  `json:"-"`
 	XXX_unrecognized         []byte                                    `json:"-"`
 	XXX_sizecache            int32                                     `json:"-"`
@@ -238,74 +239,76 @@ func init() {
 func init() { proto.RegisterFile("raft_internal.proto", fileDescriptor_b4c9a9be0cfca103) }
 
 var fileDescriptor_b4c9a9be0cfca103 = []byte{
-	// 1072 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x56, 0x4b, 0x73, 0x1b, 0x45,
-	0x17, 0x8d, 0x6c, 0xc7, 0xb6, 0x5a, 0xb6, 0xe3, 0xb4, 0x9d, 0x2f, 0xfd, 0xd9, 0x55, 0xc6, 0x71,
-	0x48, 0x30, 0x10, 0xe4, 0x20, 0xc3, 0x02, 0x36, 0xa0, 0x48, 0x2e, 0xc7, 0x54, 0x48, 0xb9, 0x26,
-	0x81, 0x4a, 0x41, 0x51, 0x43, 0x6b, 0xe6, 0x5a, 0x9a, 0x78, 0x34, 0x33, 0x74, 0xb7, 0x14, 0x67,
-	0xcb, 0x92, 0x35, 0x50, 0xfc, 0x0c, 0x9e, 0xff, 0x21, 0x45, 0xf1, 0x08, 0xf0, 0x07, 0xc0, 0x6c,
-	0xd8, 0x03, 0x7b, 0xaa, 0x1f, 0xf3, 0x92, 0x5a, 0xde, 0x8d, 0xee, 0x3d, 0xf7, 0x9c, 0xd3, 0xdd,
-	0xb7, 0x5b, 0x17, 0xad, 0x30, 0x7a, 0x24, 0xdc, 0x20, 0x12, 0xc0, 0x22, 0x1a, 0xd6, 0x13, 0x16,
-	0x8b, 0x18, 0x2f, 0x80, 0xf0, 0x7c, 0x0e, 0x6c, 0x08, 0x2c, 0xe9, 0xac, 0xad, 0x76, 0xe3, 0x6e,
-	0xac, 0x12, 0x3b, 0xf2, 0x4b, 0x63, 0xd6, 0x96, 0x73, 0x8c, 0x89, 0x54, 0x59, 0xe2, 0x99, 0xcf,
-	0x4d, 0x99, 0xdc, 0xa1, 0x49, 0xb0, 0x33, 0x04, 0xc6, 0x83, 0x38, 0x4a, 0x3a, 0xe9, 0x97, 0x41,
-	0x5c, 0xcf, 0x10, 0x7d, 0xe8, 0x77, 0x80, 0xf1, 0x5e, 0x90, 0x24, 0x9d, 0xc2, 0x0f, 0x8d, 0xdb,
-	0x62, 0x68, 0xd1, 0x81, 0x8f, 0x06, 0xc0, 0xc5, 0x6d, 0xa0, 0x3e, 0x30, 0xbc, 0x84, 0xa6, 0x0e,
-	0xda, 0xa4, 0xb2, 0x59, 0xd9, 0x9e, 0x71, 0xa6, 0x0e, 0xda, 0x78, 0x0d, 0xcd, 0x0f, 0xb8, 0x34,
-	0xdf, 0x07, 0x32, 0xb5, 0x59, 0xd9, 0xae, 0x3a, 0xd9, 0x6f, 0x7c, 0x03, 0x2d, 0xd2, 0x81, 0xe8,
-	0xb9, 0x0c, 0x86, 0x81, 0xd4, 0x26, 0xd3, 0xb2, 0xec, 0xd6, 0xdc, 0x27, 0xdf, 0x91, 0xe9, 0xdd,
-	0xfa, 0xcb, 0xce, 0x82, 0xcc, 0x3a, 0x26, 0xf9, 0xfa, 0xdc, 0xc7, 0x2a, 0x7c, 0x73, 0xeb, 0x7b,
-	0x8c, 0x56, 0x0e, 0xcc, 0x8e, 0x38, 0xf4, 0x48, 0x18, 0x03, 0x78, 0x17, 0xcd, 0xf6, 0x94, 0x09,
-	0xe2, 0x6f, 0x56, 0xb6, 0x6b, 0x8d, 0xf5, 0x7a, 0x71, 0x9f, 0xea, 0x25, 0x9f, 0x8e, 0x81, 0x8e,
-	0xf9, 0xbd, 0x86, 0xa6, 0x86, 0x0d, 0xe5, 0xb4, 0xd6, 0xb8, 0x64, 0x25, 0x70, 0xa6, 0x86, 0x0d,
-	0x7c, 0x13, 0x9d, 0x67, 0x34, 0xea, 0x82, 0xb2, 0x5c, 0x6b, 0xac, 0x8d, 0x20, 0x65, 0x2a, 0x85,
-	0x6b, 0x20, 0x7e, 0x01, 0x4d, 0x27, 0x03, 0x41, 0x66, 0x14, 0x9e, 0x94, 0xf1, 0x87, 0x83, 0x74,
-	0x11, 0x8e, 0x04, 0xe1, 0x16, 0x5a, 0xf0, 0x21, 0x04, 0x01, 0xae, 0x16, 0x39, 0xaf, 0x8a, 0x36,
-	0xcb, 0x45, 0x6d, 0x85, 0x28, 0x49, 0xd5, 0xfc, 0x3c, 0x26, 0x05, 0xc5, 0x49, 0x44, 0x66, 0x6d,
-	0x82, 0xf7, 0x4f, 0xa2, 0x4c, 0x50, 0x9c, 0x44, 0xf8, 0x0d, 0x84, 0xbc, 0xb8, 0x9f, 0x50, 0x4f,
-	0xc8, 0x63, 0x98, 0x53, 0x25, 0xcf, 0x94, 0x4b, 0x5a, 0x59, 0x3e, 0xad, 0x2c, 0x94, 0xe0, 0x37,
-	0x51, 0x2d, 0x04, 0xca, 0xc1, 0xed, 0x32, 0x1a, 0x09, 0x32, 0x6f, 0x63, 0xb8, 0x23, 0x01, 0xfb,
-	0x32, 0x9f, 0x31, 0x84, 0x59, 0x48, 0xae, 0x59, 0x33, 0x30, 0x18, 0xc6, 0xc7, 0x40, 0xaa, 0xb6,
-	0x35, 0x2b, 0x0a, 0x47, 0x01, 0xb2, 0x35, 0x87, 0x79, 0x4c, 0x1e, 0x0b, 0x0d, 0x29, 0xeb, 0x13,
-	0x64, 0x3b, 0x96, 0xa6, 0x4c, 0x65, 0xc7, 0xa2, 0x80, 0xf8, 0x01, 0x5a, 0xd6, 0xb2, 0x5e, 0x0f,
-	0xbc, 0xe3, 0x24, 0x0e, 0x22, 0x41, 0x6a, 0xaa, 0xf8, 0x59, 0x8b, 0x74, 0x2b, 0x03, 0x19, 0x9a,
-	0xb4, 0x59, 0x5f, 0x71, 0x2e, 0x84, 0x65, 0x00, 0x6e, 0xa2, 0x9a, 0xea, 0x6e, 0x88, 0x68, 0x27,
-	0x04, 0xf2, 0x97, 0x75, 0x57, 0x9b, 0x03, 0xd1, 0xdb, 0x53, 0x80, 0x6c, 0x4f, 0x68, 0x16, 0xc2,
-	0x6d, 0xa4, 0xae, 0x80, 0xeb, 0x07, 0x5c, 0x71, 0xfc, 0x3d, 0x67, 0xdb, 0x14, 0xc9, 0xd1, 0xd6,
-	0x88, 0x6c, 0x53, 0x68, 0x1e, 0xc3, 0x6f, 0x19, 0x23, 0x5c, 0x50, 0x31, 0xe0, 0xe4, 0xdf, 0x89,
-	0x46, 0xee, 0x29, 0xc0, 0xc8, 0xca, 0x5e, 0xd5, 0x8e, 0x74, 0x0e, 0xdf, 0xd5, 0x8e, 0x20, 0x12,
-	0x81, 0x47, 0x05, 0x90, 0x7f, 0x34, 0xd9, 0xf3, 0x65, 0xb2, 0xf4, 0x76, 0x36, 0x0b, 0xd0, 0xd4,
-	0x5a, 0xa9, 0x1e, 0xef, 0x99, 0x27, 0x40, 0xbe, 0x09, 0x2e, 0xf5, 0x7d, 0xf2, 0xc3, 0xfc, 0xa4,
-	0x25, 0xbe, 0xc3, 0x81, 0x35, 0x7d, 0xbf, 0xb4, 0x44, 0x13, 0xc3, 0x77, 0xd1, 0x72, 0x4e, 0xa3,
-	0x2f, 0x01, 0xf9, 0x51, 0x33, 0x5d, 0xb5, 0x33, 0x99, 0xdb, 0x63, 0xc8, 0x96, 0x68, 0x29, 0x5c,
-	0xb6, 0xd5, 0x05, 0x41, 0x7e, 0x3a, 0xd3, 0xd6, 0x3e, 0x88, 0x31, 0x5b, 0xfb, 0x20, 0x70, 0x17,
-	0xfd, 0x3f, 0xa7, 0xf1, 0x7a, 0xf2, 0x5a, 0xba, 0x09, 0xe5, 0xfc, 0x51, 0xcc, 0x7c, 0xf2, 0xb3,
-	0xa6, 0x7c, 0xd1, 0x4e, 0xd9, 0x52, 0xe8, 0x43, 0x03, 0x4e, 0xd9, 0xff, 0x47, 0xad, 0x69, 0xfc,
-	0x00, 0xad, 0x16, 0xfc, 0xca, 0xfb, 0xe4, 0xb2, 0x38, 0x04, 0xf2, 0x54, 0x6b, 0x5c, 0x9f, 0x60,
-	0x5b, 0xdd, 0xc5, 0x38, 0x6f, 0x9b, 0x8b, 0x74, 0x34, 0x83, 0xdf, 0x47, 0x97, 0x72, 0x66, 0x7d,
-	0x35, 0x35, 0xf5, 0x2f, 0x9a, 0xfa, 0x39, 0x3b, 0xb5, 0xb9, 0xa3, 0x05, 0x6e, 0x4c, 0xc7, 0x52,
-	0xf8, 0x36, 0x5a, 0xca, 0xc9, 0xc3, 0x80, 0x0b, 0xf2, 0xab, 0x66, 0xbd, 0x62, 0x67, 0xbd, 0x13,
-	0x70, 0x51, 0xea, 0xa3, 0x34, 0x98, 0x31, 0x49, 0x6b, 0x9a, 0xe9, 0xb7, 0x89, 0x4c, 0x52, 0x7a,
-	0x8c, 0x29, 0x0d, 0x66, 0x47, 0xaf, 0x98, 0x64, 0x47, 0x7e, 0x59, 0x9d, 0x74, 0xf4, 0xb2, 0x66,
-	0xb4, 0x23, 0x4d, 0x2c, 0xeb, 0x48, 0x45, 0x63, 0x3a, 0xf2, 0xab, 0xea, 0xa4, 0x8e, 0x94, 0x55,
-	0x96, 0x8e, 0xcc, 0xc3, 0x65, 0x5b, 0xb2, 0x23, 0xbf, 0x3e, 0xd3, 0xd6, 0x68, 0x47, 0x9a, 0x18,
-	0x7e, 0x88, 0xd6, 0x0a, 0x34, 0xaa, 0x51, 0x12, 0x60, 0xfd, 0x80, 0xab, 0xff, 0xdf, 0x6f, 0x34,
-	0xe7, 0x8d, 0x09, 0x9c, 0x12, 0x7e, 0x98, 0xa1, 0x53, 0xfe, 0xcb, 0xd4, 0x9e, 0xc7, 0x7d, 0xb4,
-	0x9e, 0x6b, 0x99, 0xd6, 0x29, 0x88, 0x7d, 0xab, 0xc5, 0x5e, 0xb2, 0x8b, 0xe9, 0x2e, 0x19, 0x57,
-	0x23, 0x74, 0x02, 0x00, 0x7f, 0x88, 0x56, 0xbc, 0x70, 0xc0, 0x05, 0x30, 0xd7, 0xcc, 0x32, 0x2e,
-	0x07, 0x41, 0x3e, 0x45, 0xe6, 0x0a, 0x14, 0x07, 0x99, 0x7a, 0x4b, 0x23, 0xdf, 0xd5, 0xc0, 0x7b,
-	0x20, 0xc6, 0x5e, 0xbd, 0x8b, 0xde, 0x28, 0x04, 0x3f, 0x44, 0x97, 0x53, 0x05, 0x4d, 0xe6, 0x52,
-	0x21, 0x98, 0x52, 0xf9, 0x0c, 0x99, 0x77, 0xd0, 0xa6, 0xf2, 0xb6, 0x8a, 0x35, 0x85, 0x60, 0x36,
-	0xa1, 0x55, 0xcf, 0x82, 0xc2, 0x1f, 0x20, 0xec, 0xc7, 0x8f, 0xa2, 0x2e, 0xa3, 0x3e, 0xb8, 0x41,
-	0x74, 0x14, 0x2b, 0x99, 0xcf, 0xb5, 0xcc, 0xb5, 0xb2, 0x4c, 0x3b, 0x05, 0x1e, 0x44, 0x47, 0xb1,
-	0x4d, 0x62, 0xd9, 0x1f, 0x41, 0xe4, 0xc3, 0xd4, 0x05, 0xb4, 0xb8, 0xd7, 0x4f, 0xc4, 0x63, 0x07,
-	0x78, 0x12, 0x47, 0x1c, 0xb6, 0x1e, 0xa3, 0xf5, 0x33, 0x9e, 0x6f, 0x8c, 0xd1, 0x8c, 0x9a, 0xe5,
-	0x2a, 0x6a, 0x96, 0x53, 0xdf, 0x72, 0xc6, 0xcb, 0x5e, 0x35, 0x33, 0xe3, 0xa5, 0xbf, 0xf1, 0x15,
-	0xb4, 0xc0, 0x83, 0x7e, 0x12, 0x82, 0x2b, 0xe2, 0x63, 0xd0, 0x23, 0x5e, 0xd5, 0xa9, 0xe9, 0xd8,
-	0x7d, 0x19, 0xca, 0xbc, 0xdc, 0x7a, 0xed, 0xc9, 0x1f, 0x1b, 0xe7, 0x9e, 0x9c, 0x6e, 0x54, 0x9e,
-	0x9e, 0x6e, 0x54, 0x7e, 0x3f, 0xdd, 0xa8, 0x7c, 0xf1, 0xe7, 0xc6, 0xb9, 0xf7, 0xae, 0x76, 0x63,
-	0xd5, 0x22, 0xf5, 0x20, 0xde, 0xc9, 0xe7, 0xd6, 0xdd, 0x9d, 0x62, 0xdb, 0x74, 0x66, 0xd5, 0x38,
-	0xba, 0xfb, 0x5f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x06, 0x59, 0x70, 0x10, 0x30, 0x0b, 0x00, 0x00,
+	// 1101 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x56, 0xcb, 0x72, 0x1b, 0x45,
+	0x14, 0x8d, 0x6c, 0xc7, 0xb6, 0x5a, 0xb6, 0xe3, 0xb4, 0x9d, 0xa4, 0xb1, 0xab, 0x8c, 0xe3, 0x90,
+	0x60, 0x20, 0xc8, 0xc1, 0x06, 0xaa, 0x60, 0x03, 0x8a, 0xe5, 0x72, 0x4c, 0x25, 0x29, 0xd7, 0xc4,
+	0x50, 0x29, 0x28, 0x6a, 0x68, 0xcd, 0x5c, 0x4b, 0x13, 0x8f, 0x66, 0x86, 0xee, 0x96, 0xe2, 0x6c,
+	0x59, 0xb2, 0x06, 0x8a, 0x8f, 0x60, 0xc1, 0x2b, 0xff, 0x90, 0x05, 0x8f, 0x00, 0x3f, 0x00, 0x66,
+	0xc3, 0x1e, 0xd8, 0xa7, 0xfa, 0x31, 0x2f, 0xa9, 0xe5, 0xdd, 0xe8, 0xde, 0x73, 0xcf, 0x39, 0xdd,
+	0x7d, 0xbb, 0x75, 0xd1, 0x02, 0xa3, 0x87, 0xc2, 0x0d, 0x22, 0x01, 0x2c, 0xa2, 0x61, 0x3d, 0x61,
+	0xb1, 0x88, 0xf1, 0x0c, 0x08, 0xcf, 0xe7, 0xc0, 0xfa, 0xc0, 0x92, 0xd6, 0xd2, 0x62, 0x3b, 0x6e,
+	0xc7, 0x2a, 0xb1, 0x21, 0xbf, 0x34, 0x66, 0x69, 0x3e, 0xc7, 0x98, 0x48, 0x95, 0x25, 0x9e, 0xf9,
+	0x5c, 0x95, 0xc9, 0x0d, 0x9a, 0x04, 0x1b, 0x7d, 0x60, 0x3c, 0x88, 0xa3, 0xa4, 0x95, 0x7e, 0x19,
+	0xc4, 0xb5, 0x0c, 0xd1, 0x85, 0x6e, 0x0b, 0x18, 0xef, 0x04, 0x49, 0xd2, 0x2a, 0xfc, 0xd0, 0xb8,
+	0x35, 0x86, 0x66, 0x1d, 0xf8, 0xb4, 0x07, 0x5c, 0xdc, 0x02, 0xea, 0x03, 0xc3, 0x73, 0x68, 0x6c,
+	0xaf, 0x49, 0x2a, 0xab, 0x95, 0xf5, 0x09, 0x67, 0x6c, 0xaf, 0x89, 0x97, 0xd0, 0x74, 0x8f, 0x4b,
+	0xf3, 0x5d, 0x20, 0x63, 0xab, 0x95, 0xf5, 0xaa, 0x93, 0xfd, 0xc6, 0xd7, 0xd1, 0x2c, 0xed, 0x89,
+	0x8e, 0xcb, 0xa0, 0x1f, 0x48, 0x6d, 0x32, 0x2e, 0xcb, 0x6e, 0x4e, 0x7d, 0xfe, 0x98, 0x8c, 0x6f,
+	0xd5, 0x5f, 0x73, 0x66, 0x64, 0xd6, 0x31, 0xc9, 0xb7, 0xa7, 0x3e, 0x53, 0xe1, 0x1b, 0x6b, 0x8f,
+	0x17, 0xd0, 0xc2, 0x9e, 0xd9, 0x11, 0x87, 0x1e, 0x0a, 0x63, 0x00, 0x6f, 0xa1, 0xc9, 0x8e, 0x32,
+	0x41, 0xfc, 0xd5, 0xca, 0x7a, 0x6d, 0x73, 0xb9, 0x5e, 0xdc, 0xa7, 0x7a, 0xc9, 0xa7, 0x63, 0xa0,
+	0x43, 0x7e, 0xaf, 0xa2, 0xb1, 0xfe, 0xa6, 0x72, 0x5a, 0xdb, 0xbc, 0x60, 0x25, 0x70, 0xc6, 0xfa,
+	0x9b, 0xf8, 0x06, 0x3a, 0xcb, 0x68, 0xd4, 0x06, 0x65, 0xb9, 0xb6, 0xb9, 0x34, 0x80, 0x94, 0xa9,
+	0x14, 0xae, 0x81, 0xf8, 0x65, 0x34, 0x9e, 0xf4, 0x04, 0x99, 0x50, 0x78, 0x52, 0xc6, 0xef, 0xf7,
+	0xd2, 0x45, 0x38, 0x12, 0x84, 0xb7, 0xd1, 0x8c, 0x0f, 0x21, 0x08, 0x70, 0xb5, 0xc8, 0x59, 0x55,
+	0xb4, 0x5a, 0x2e, 0x6a, 0x2a, 0x44, 0x49, 0xaa, 0xe6, 0xe7, 0x31, 0x29, 0x28, 0x8e, 0x23, 0x32,
+	0x69, 0x13, 0x3c, 0x38, 0x8e, 0x32, 0x41, 0x71, 0x1c, 0xe1, 0x77, 0x10, 0xf2, 0xe2, 0x6e, 0x42,
+	0x3d, 0x21, 0x8f, 0x61, 0x4a, 0x95, 0x3c, 0x5f, 0x2e, 0xd9, 0xce, 0xf2, 0x69, 0x65, 0xa1, 0x04,
+	0xbf, 0x8b, 0x6a, 0x21, 0x50, 0x0e, 0x6e, 0x9b, 0xd1, 0x48, 0x90, 0x69, 0x1b, 0xc3, 0x6d, 0x09,
+	0xd8, 0x95, 0xf9, 0x8c, 0x21, 0xcc, 0x42, 0x72, 0xcd, 0x9a, 0x81, 0x41, 0x3f, 0x3e, 0x02, 0x52,
+	0xb5, 0xad, 0x59, 0x51, 0x38, 0x0a, 0x90, 0xad, 0x39, 0xcc, 0x63, 0xf2, 0x58, 0x68, 0x48, 0x59,
+	0x97, 0x20, 0xdb, 0xb1, 0x34, 0x64, 0x2a, 0x3b, 0x16, 0x05, 0xc4, 0xf7, 0xd1, 0xbc, 0x96, 0xf5,
+	0x3a, 0xe0, 0x1d, 0x25, 0x71, 0x10, 0x09, 0x52, 0x53, 0xc5, 0x2f, 0x58, 0xa4, 0xb7, 0x33, 0x90,
+	0xa1, 0x49, 0x9b, 0xf5, 0x75, 0xe7, 0x5c, 0x58, 0x06, 0xe0, 0x06, 0xaa, 0xa9, 0xee, 0x86, 0x88,
+	0xb6, 0x42, 0x20, 0xff, 0x58, 0x77, 0xb5, 0xd1, 0x13, 0x9d, 0x1d, 0x05, 0xc8, 0xf6, 0x84, 0x66,
+	0x21, 0xdc, 0x44, 0xea, 0x0a, 0xb8, 0x7e, 0xc0, 0x15, 0xc7, 0xbf, 0x53, 0xb6, 0x4d, 0x91, 0x1c,
+	0x4d, 0x8d, 0xc8, 0x36, 0x85, 0xe6, 0x31, 0xfc, 0x9e, 0x31, 0xc2, 0x05, 0x15, 0x3d, 0x4e, 0xfe,
+	0x1f, 0x69, 0xe4, 0x9e, 0x02, 0x0c, 0xac, 0xec, 0x0d, 0xed, 0x48, 0xe7, 0xf0, 0x5d, 0xed, 0x08,
+	0x22, 0x11, 0x78, 0x54, 0x00, 0xf9, 0x4f, 0x93, 0xbd, 0x54, 0x26, 0x4b, 0x6f, 0x67, 0xa3, 0x00,
+	0x4d, 0xad, 0x95, 0xea, 0xf1, 0x8e, 0x79, 0x02, 0xe4, 0x9b, 0xe0, 0x52, 0xdf, 0x27, 0x3f, 0x4d,
+	0x8f, 0x5a, 0xe2, 0xfb, 0x1c, 0x58, 0xc3, 0xf7, 0x4b, 0x4b, 0x34, 0x31, 0x7c, 0x17, 0xcd, 0xe7,
+	0x34, 0xfa, 0x12, 0x90, 0x9f, 0x35, 0xd3, 0x15, 0x3b, 0x93, 0xb9, 0x3d, 0x86, 0x6c, 0x8e, 0x96,
+	0xc2, 0x65, 0x5b, 0x6d, 0x10, 0xe4, 0x97, 0x53, 0x6d, 0xed, 0x82, 0x18, 0xb2, 0xb5, 0x0b, 0x02,
+	0xb7, 0xd1, 0x73, 0x39, 0x8d, 0xd7, 0x91, 0xd7, 0xd2, 0x4d, 0x28, 0xe7, 0x0f, 0x63, 0xe6, 0x93,
+	0x5f, 0x35, 0xe5, 0x2b, 0x76, 0xca, 0x6d, 0x85, 0xde, 0x37, 0xe0, 0x94, 0xfd, 0x22, 0xb5, 0xa6,
+	0xf1, 0x7d, 0xb4, 0x58, 0xf0, 0x2b, 0xef, 0x93, 0xcb, 0xe2, 0x10, 0xc8, 0x53, 0xad, 0x71, 0x6d,
+	0x84, 0x6d, 0x75, 0x17, 0xe3, 0xbc, 0x6d, 0xce, 0xd3, 0xc1, 0x0c, 0xfe, 0x08, 0x5d, 0xc8, 0x99,
+	0xf5, 0xd5, 0xd4, 0xd4, 0xbf, 0x69, 0xea, 0x17, 0xed, 0xd4, 0xe6, 0x8e, 0x16, 0xb8, 0x31, 0x1d,
+	0x4a, 0xe1, 0x5b, 0x68, 0x2e, 0x27, 0x0f, 0x03, 0x2e, 0xc8, 0xef, 0x9a, 0xf5, 0xb2, 0x9d, 0xf5,
+	0x76, 0xc0, 0x45, 0xa9, 0x8f, 0xd2, 0x60, 0xc6, 0x24, 0xad, 0x69, 0xa6, 0x3f, 0x46, 0x32, 0x49,
+	0xe9, 0x21, 0xa6, 0x34, 0x98, 0x1d, 0xbd, 0x62, 0x92, 0x1d, 0xf9, 0x6d, 0x75, 0xd4, 0xd1, 0xcb,
+	0x9a, 0xc1, 0x8e, 0x34, 0xb1, 0xac, 0x23, 0x15, 0x8d, 0xe9, 0xc8, 0xef, 0xaa, 0xa3, 0x3a, 0x52,
+	0x56, 0x59, 0x3a, 0x32, 0x0f, 0x97, 0x6d, 0xc9, 0x8e, 0xfc, 0xfe, 0x54, 0x5b, 0x83, 0x1d, 0x69,
+	0x62, 0xf8, 0x01, 0x5a, 0x2a, 0xd0, 0xa8, 0x46, 0x49, 0x80, 0x75, 0x03, 0xae, 0xfe, 0x7f, 0x7f,
+	0xd0, 0x9c, 0xd7, 0x47, 0x70, 0x4a, 0xf8, 0x7e, 0x86, 0x4e, 0xf9, 0x2f, 0x51, 0x7b, 0x1e, 0x77,
+	0xd1, 0x72, 0xae, 0x65, 0x5a, 0xa7, 0x20, 0xf6, 0xa3, 0x16, 0x7b, 0xd5, 0x2e, 0xa6, 0xbb, 0x64,
+	0x58, 0x8d, 0xd0, 0x11, 0x00, 0xfc, 0x09, 0x5a, 0xf0, 0xc2, 0x1e, 0x17, 0xc0, 0x5c, 0x33, 0xcb,
+	0xb8, 0x1c, 0x04, 0xf9, 0x02, 0x99, 0x2b, 0x50, 0x1c, 0x64, 0xea, 0xdb, 0x1a, 0xf9, 0x81, 0x06,
+	0xde, 0x03, 0x31, 0xf4, 0xea, 0x9d, 0xf7, 0x06, 0x21, 0xf8, 0x01, 0xba, 0x94, 0x2a, 0x68, 0x32,
+	0x97, 0x0a, 0xc1, 0x94, 0xca, 0x97, 0xc8, 0xbc, 0x83, 0x36, 0x95, 0x3b, 0x2a, 0xd6, 0x10, 0x82,
+	0xd9, 0x84, 0x16, 0x3d, 0x0b, 0x0a, 0x7f, 0x8c, 0xb0, 0x1f, 0x3f, 0x8c, 0xda, 0x8c, 0xfa, 0xe0,
+	0x06, 0xd1, 0x61, 0xac, 0x64, 0xbe, 0xd2, 0x32, 0x57, 0xcb, 0x32, 0xcd, 0x14, 0xb8, 0x17, 0x1d,
+	0xc6, 0x36, 0x89, 0x79, 0x7f, 0x00, 0x81, 0x03, 0x74, 0x31, 0xa7, 0x4f, 0xb7, 0x4b, 0x00, 0x17,
+	0xe4, 0x9b, 0x3b, 0xb6, 0x17, 0x3d, 0x93, 0x30, 0xdb, 0x71, 0x00, 0x7c, 0x50, 0xe6, 0x4d, 0x67,
+	0xd1, 0xb7, 0xa0, 0xf2, 0xb9, 0xed, 0x1c, 0x9a, 0xdd, 0xe9, 0x26, 0xe2, 0x91, 0x03, 0x3c, 0x89,
+	0x23, 0x0e, 0x6b, 0x8f, 0xd0, 0xf2, 0x29, 0xff, 0x14, 0x18, 0xa3, 0x09, 0x35, 0x36, 0x56, 0xd4,
+	0xd8, 0xa8, 0xbe, 0xe5, 0x38, 0x99, 0x3d, 0xa0, 0x66, 0x9c, 0x4c, 0x7f, 0xe3, 0xcb, 0x68, 0x86,
+	0x07, 0xdd, 0x24, 0x04, 0x57, 0xc4, 0x47, 0xa0, 0xa7, 0xc9, 0xaa, 0x53, 0xd3, 0xb1, 0x03, 0x19,
+	0xca, 0xbc, 0xdc, 0x7c, 0xeb, 0xc9, 0x5f, 0x2b, 0x67, 0x9e, 0x9c, 0xac, 0x54, 0x9e, 0x9e, 0xac,
+	0x54, 0xfe, 0x3c, 0x59, 0xa9, 0x7c, 0xfd, 0xf7, 0xca, 0x99, 0x0f, 0xaf, 0xb4, 0x63, 0xb5, 0xec,
+	0x7a, 0x10, 0x6f, 0xe4, 0x23, 0xf2, 0xd6, 0x46, 0x71, 0x2b, 0x5a, 0x93, 0x6a, 0xf2, 0xdd, 0x7a,
+	0x16, 0x00, 0x00, 0xff, 0xff, 0xb2, 0xa0, 0x15, 0x1f, 0x9b, 0x0b, 0x00, 0x00,
 }
 
 func (m *RequestHeader) Marshal() (dAtA []byte, err error) {
@@ -376,6 +379,22 @@ func (m *InternalRaftRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 		i -= len(m.XXX_unrecognized)
 		copy(dAtA[i:], m.XXX_unrecognized)
 	}
+	if m.DowngradeVersionTest != nil {
+		{
+			size, err := m.DowngradeVersionTest.MarshalToSizedBuffer(dAtA[:i])
+			if err != nil {
+				return 0, err
+			}
+			i -= size
+			i = encodeVarintRaftInternal(dAtA, i, uint64(size))
+		}
+		i--
+		dAtA[i] = 0x4
+		i--
+		dAtA[i] = 0xea
+		i--
+		dAtA[i] = 0xe2
+	}
 	if m.DowngradeInfoSet != nil {
 		{
 			size, err := m.DowngradeInfoSet.MarshalToSizedBuffer(dAtA[:i])
@@ -1039,6 +1058,10 @@ func (m *InternalRaftRequest) Size() (n int) {
 		l = m.DowngradeInfoSet.Size()
 		n += 2 + l + sovRaftInternal(uint64(l))
 	}
+	if m.DowngradeVersionTest != nil {
+		l = m.DowngradeVersionTest.Size()
+		n += 3 + l + sovRaftInternal(uint64(l))
+	}
 	if m.XXX_unrecognized != nil {
 		n += len(m.XXX_unrecognized)
 	}
@@ -2372,6 +2395,42 @@ func (m *InternalRaftRequest) Unmarshal(dAtA []byte) error {
 				return err
 			}
 			iNdEx = postIndex
+		case 9900:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field DowngradeVersionTest", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowRaftInternal
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				msglen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthRaftInternal
+			}
+			postIndex := iNdEx + msglen
+			if postIndex < 0 {
+				return ErrInvalidLengthRaftInternal
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			if m.DowngradeVersionTest == nil {
+				m.DowngradeVersionTest = &DowngradeVersionTestRequest{}
+			}
+			if err := m.DowngradeVersionTest.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
 		default:
 			iNdEx = preIndex
 			skippy, err := skipRaftInternal(dAtA[iNdEx:])
diff --git a/api/etcdserverpb/raft_internal.proto b/api/etcdserverpb/raft_internal.proto
index 91f7705b7..88b8ab527 100644
--- a/api/etcdserverpb/raft_internal.proto
+++ b/api/etcdserverpb/raft_internal.proto
@@ -71,6 +71,8 @@ message InternalRaftRequest {
   membershippb.ClusterVersionSetRequest cluster_version_set = 1300 [(versionpb.etcd_version_field) = "3.5"];
   membershippb.ClusterMemberAttrSetRequest cluster_member_attr_set = 1301 [(versionpb.etcd_version_field) = "3.5"];
   membershippb.DowngradeInfoSetRequest  downgrade_info_set = 1302 [(versionpb.etcd_version_field) = "3.5"];
+
+  DowngradeVersionTestRequest downgrade_version_test = 9900 [(versionpb.etcd_version_field) = "3.6"];
 }
 
 message EmptyResponse {
diff --git a/api/etcdserverpb/rpc.pb.go b/api/etcdserverpb/rpc.pb.go
index 87aec5c01..b313cc59a 100644
--- a/api/etcdserverpb/rpc.pb.go
+++ b/api/etcdserverpb/rpc.pb.go
@@ -4189,6 +4189,57 @@ func (m *DowngradeResponse) GetVersion() string {
 	return ""
 }
 
+// DowngradeVersionTestRequest is used for test only. The version in
+// this request will be read as the WAL record version.If the downgrade
+// target version is less than this version, then the downgrade(online)
+// or migration(offline) isn't safe, so shouldn't be allowed.
+type DowngradeVersionTestRequest struct {
+	Ver                  string   `protobuf:"bytes,1,opt,name=ver,proto3" json:"ver,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *DowngradeVersionTestRequest) Reset()         { *m = DowngradeVersionTestRequest{} }
+func (m *DowngradeVersionTestRequest) String() string { return proto.CompactTextString(m) }
+func (*DowngradeVersionTestRequest) ProtoMessage()    {}
+func (*DowngradeVersionTestRequest) Descriptor() ([]byte, []int) {
+	return fileDescriptor_77a6da22d6a3feb1, []int{59}
+}
+func (m *DowngradeVersionTestRequest) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *DowngradeVersionTestRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_DowngradeVersionTestRequest.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *DowngradeVersionTestRequest) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_DowngradeVersionTestRequest.Merge(m, src)
+}
+func (m *DowngradeVersionTestRequest) XXX_Size() int {
+	return m.Size()
+}
+func (m *DowngradeVersionTestRequest) XXX_DiscardUnknown() {
+	xxx_messageInfo_DowngradeVersionTestRequest.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_DowngradeVersionTestRequest proto.InternalMessageInfo
+
+func (m *DowngradeVersionTestRequest) GetVer() string {
+	if m != nil {
+		return m.Ver
+	}
+	return ""
+}
+
 type StatusRequest struct {
 	XXX_NoUnkeyedLiteral struct{} `json:"-"`
 	XXX_unrecognized     []byte   `json:"-"`
@@ -4199,7 +4250,7 @@ func (m *StatusRequest) Reset()         { *m = StatusRequest{} }
 func (m *StatusRequest) String() string { return proto.CompactTextString(m) }
 func (*StatusRequest) ProtoMessage()    {}
 func (*StatusRequest) Descriptor() ([]byte, []int) {
-	return fileDescriptor_77a6da22d6a3feb1, []int{59}
+	return fileDescriptor_77a6da22d6a3feb1, []int{60}
 }
 func (m *StatusRequest) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -4261,7 +4312,7 @@ func (m *StatusResponse) Reset()         { *m = StatusResponse{} }
 func (m *StatusResponse) String() string { return proto.CompactTextString(m) }
 func (*StatusResponse) ProtoMessage()    {}
 func (*StatusResponse) Descriptor() ([]byte, []int) {
-	return fileDescriptor_77a6da22d6a3feb1, []int{60}
+	return fileDescriptor_77a6da22d6a3feb1, []int{61}
 }
 func (m *StatusResponse) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -4384,7 +4435,7 @@ func (m *AuthEnableRequest) Reset()         { *m = AuthEnableRequest{} }
 func (m *AuthEnableRequest) String() string { return proto.CompactTextString(m) }
 func (*AuthEnableRequest) ProtoMessage()    {}
 func (*AuthEnableRequest) Descriptor() ([]byte, []int) {
-	return fileDescriptor_77a6da22d6a3feb1, []int{61}
+	return fileDescriptor_77a6da22d6a3feb1, []int{62}
 }
 func (m *AuthEnableRequest) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -4423,7 +4474,7 @@ func (m *AuthDisableRequest) Reset()         { *m = AuthDisableRequest{} }
 func (m *AuthDisableRequest) String() string { return proto.CompactTextString(m) }
 func (*AuthDisableRequest) ProtoMessage()    {}
 func (*AuthDisableRequest) Descriptor() ([]byte, []int) {
-	return fileDescriptor_77a6da22d6a3feb1, []int{62}
+	return fileDescriptor_77a6da22d6a3feb1, []int{63}
 }
 func (m *AuthDisableRequest) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -4462,7 +4513,7 @@ func (m *AuthStatusRequest) Reset()         { *m = AuthStatusRequest{} }
 func (m *AuthStatusRequest) String() string { return proto.CompactTextString(m) }
 func (*AuthStatusRequest) ProtoMessage()    {}
 func (*AuthStatusRequest) Descriptor() ([]byte, []int) {
-	return fileDescriptor_77a6da22d6a3feb1, []int{63}
+	return fileDescriptor_77a6da22d6a3feb1, []int{64}
 }
 func (m *AuthStatusRequest) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -4503,7 +4554,7 @@ func (m *AuthenticateRequest) Reset()         { *m = AuthenticateRequest{} }
 func (m *AuthenticateRequest) String() string { return proto.CompactTextString(m) }
 func (*AuthenticateRequest) ProtoMessage()    {}
 func (*AuthenticateRequest) Descriptor() ([]byte, []int) {
-	return fileDescriptor_77a6da22d6a3feb1, []int{64}
+	return fileDescriptor_77a6da22d6a3feb1, []int{65}
 }
 func (m *AuthenticateRequest) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -4560,7 +4611,7 @@ func (m *AuthUserAddRequest) Reset()         { *m = AuthUserAddRequest{} }
 func (m *AuthUserAddRequest) String() string { return proto.CompactTextString(m) }
 func (*AuthUserAddRequest) ProtoMessage()    {}
 func (*AuthUserAddRequest) Descriptor() ([]byte, []int) {
-	return fileDescriptor_77a6da22d6a3feb1, []int{65}
+	return fileDescriptor_77a6da22d6a3feb1, []int{66}
 }
 func (m *AuthUserAddRequest) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -4628,7 +4679,7 @@ func (m *AuthUserGetRequest) Reset()         { *m = AuthUserGetRequest{} }
 func (m *AuthUserGetRequest) String() string { return proto.CompactTextString(m) }
 func (*AuthUserGetRequest) ProtoMessage()    {}
 func (*AuthUserGetRequest) Descriptor() ([]byte, []int) {
-	return fileDescriptor_77a6da22d6a3feb1, []int{66}
+	return fileDescriptor_77a6da22d6a3feb1, []int{67}
 }
 func (m *AuthUserGetRequest) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -4676,7 +4727,7 @@ func (m *AuthUserDeleteRequest) Reset()         { *m = AuthUserDeleteRequest{} }
 func (m *AuthUserDeleteRequest) String() string { return proto.CompactTextString(m) }
 func (*AuthUserDeleteRequest) ProtoMessage()    {}
 func (*AuthUserDeleteRequest) Descriptor() ([]byte, []int) {
-	return fileDescriptor_77a6da22d6a3feb1, []int{67}
+	return fileDescriptor_77a6da22d6a3feb1, []int{68}
 }
 func (m *AuthUserDeleteRequest) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -4728,7 +4779,7 @@ func (m *AuthUserChangePasswordRequest) Reset()         { *m = AuthUserChangePas
 func (m *AuthUserChangePasswordRequest) String() string { return proto.CompactTextString(m) }
 func (*AuthUserChangePasswordRequest) ProtoMessage()    {}
 func (*AuthUserChangePasswordRequest) Descriptor() ([]byte, []int) {
-	return fileDescriptor_77a6da22d6a3feb1, []int{68}
+	return fileDescriptor_77a6da22d6a3feb1, []int{69}
 }
 func (m *AuthUserChangePasswordRequest) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -4792,7 +4843,7 @@ func (m *AuthUserGrantRoleRequest) Reset()         { *m = AuthUserGrantRoleReque
 func (m *AuthUserGrantRoleRequest) String() string { return proto.CompactTextString(m) }
 func (*AuthUserGrantRoleRequest) ProtoMessage()    {}
 func (*AuthUserGrantRoleRequest) Descriptor() ([]byte, []int) {
-	return fileDescriptor_77a6da22d6a3feb1, []int{69}
+	return fileDescriptor_77a6da22d6a3feb1, []int{70}
 }
 func (m *AuthUserGrantRoleRequest) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -4847,7 +4898,7 @@ func (m *AuthUserRevokeRoleRequest) Reset()         { *m = AuthUserRevokeRoleReq
 func (m *AuthUserRevokeRoleRequest) String() string { return proto.CompactTextString(m) }
 func (*AuthUserRevokeRoleRequest) ProtoMessage()    {}
 func (*AuthUserRevokeRoleRequest) Descriptor() ([]byte, []int) {
-	return fileDescriptor_77a6da22d6a3feb1, []int{70}
+	return fileDescriptor_77a6da22d6a3feb1, []int{71}
 }
 func (m *AuthUserRevokeRoleRequest) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -4902,7 +4953,7 @@ func (m *AuthRoleAddRequest) Reset()         { *m = AuthRoleAddRequest{} }
 func (m *AuthRoleAddRequest) String() string { return proto.CompactTextString(m) }
 func (*AuthRoleAddRequest) ProtoMessage()    {}
 func (*AuthRoleAddRequest) Descriptor() ([]byte, []int) {
-	return fileDescriptor_77a6da22d6a3feb1, []int{71}
+	return fileDescriptor_77a6da22d6a3feb1, []int{72}
 }
 func (m *AuthRoleAddRequest) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -4949,7 +5000,7 @@ func (m *AuthRoleGetRequest) Reset()         { *m = AuthRoleGetRequest{} }
 func (m *AuthRoleGetRequest) String() string { return proto.CompactTextString(m) }
 func (*AuthRoleGetRequest) ProtoMessage()    {}
 func (*AuthRoleGetRequest) Descriptor() ([]byte, []int) {
-	return fileDescriptor_77a6da22d6a3feb1, []int{72}
+	return fileDescriptor_77a6da22d6a3feb1, []int{73}
 }
 func (m *AuthRoleGetRequest) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -4995,7 +5046,7 @@ func (m *AuthUserListRequest) Reset()         { *m = AuthUserListRequest{} }
 func (m *AuthUserListRequest) String() string { return proto.CompactTextString(m) }
 func (*AuthUserListRequest) ProtoMessage()    {}
 func (*AuthUserListRequest) Descriptor() ([]byte, []int) {
-	return fileDescriptor_77a6da22d6a3feb1, []int{73}
+	return fileDescriptor_77a6da22d6a3feb1, []int{74}
 }
 func (m *AuthUserListRequest) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -5034,7 +5085,7 @@ func (m *AuthRoleListRequest) Reset()         { *m = AuthRoleListRequest{} }
 func (m *AuthRoleListRequest) String() string { return proto.CompactTextString(m) }
 func (*AuthRoleListRequest) ProtoMessage()    {}
 func (*AuthRoleListRequest) Descriptor() ([]byte, []int) {
-	return fileDescriptor_77a6da22d6a3feb1, []int{74}
+	return fileDescriptor_77a6da22d6a3feb1, []int{75}
 }
 func (m *AuthRoleListRequest) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -5074,7 +5125,7 @@ func (m *AuthRoleDeleteRequest) Reset()         { *m = AuthRoleDeleteRequest{} }
 func (m *AuthRoleDeleteRequest) String() string { return proto.CompactTextString(m) }
 func (*AuthRoleDeleteRequest) ProtoMessage()    {}
 func (*AuthRoleDeleteRequest) Descriptor() ([]byte, []int) {
-	return fileDescriptor_77a6da22d6a3feb1, []int{75}
+	return fileDescriptor_77a6da22d6a3feb1, []int{76}
 }
 func (m *AuthRoleDeleteRequest) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -5124,7 +5175,7 @@ func (m *AuthRoleGrantPermissionRequest) Reset()         { *m = AuthRoleGrantPer
 func (m *AuthRoleGrantPermissionRequest) String() string { return proto.CompactTextString(m) }
 func (*AuthRoleGrantPermissionRequest) ProtoMessage()    {}
 func (*AuthRoleGrantPermissionRequest) Descriptor() ([]byte, []int) {
-	return fileDescriptor_77a6da22d6a3feb1, []int{76}
+	return fileDescriptor_77a6da22d6a3feb1, []int{77}
 }
 func (m *AuthRoleGrantPermissionRequest) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -5180,7 +5231,7 @@ func (m *AuthRoleRevokePermissionRequest) Reset()         { *m = AuthRoleRevokeP
 func (m *AuthRoleRevokePermissionRequest) String() string { return proto.CompactTextString(m) }
 func (*AuthRoleRevokePermissionRequest) ProtoMessage()    {}
 func (*AuthRoleRevokePermissionRequest) Descriptor() ([]byte, []int) {
-	return fileDescriptor_77a6da22d6a3feb1, []int{77}
+	return fileDescriptor_77a6da22d6a3feb1, []int{78}
 }
 func (m *AuthRoleRevokePermissionRequest) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -5241,7 +5292,7 @@ func (m *AuthEnableResponse) Reset()         { *m = AuthEnableResponse{} }
 func (m *AuthEnableResponse) String() string { return proto.CompactTextString(m) }
 func (*AuthEnableResponse) ProtoMessage()    {}
 func (*AuthEnableResponse) Descriptor() ([]byte, []int) {
-	return fileDescriptor_77a6da22d6a3feb1, []int{78}
+	return fileDescriptor_77a6da22d6a3feb1, []int{79}
 }
 func (m *AuthEnableResponse) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -5288,7 +5339,7 @@ func (m *AuthDisableResponse) Reset()         { *m = AuthDisableResponse{} }
 func (m *AuthDisableResponse) String() string { return proto.CompactTextString(m) }
 func (*AuthDisableResponse) ProtoMessage()    {}
 func (*AuthDisableResponse) Descriptor() ([]byte, []int) {
-	return fileDescriptor_77a6da22d6a3feb1, []int{79}
+	return fileDescriptor_77a6da22d6a3feb1, []int{80}
 }
 func (m *AuthDisableResponse) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -5338,7 +5389,7 @@ func (m *AuthStatusResponse) Reset()         { *m = AuthStatusResponse{} }
 func (m *AuthStatusResponse) String() string { return proto.CompactTextString(m) }
 func (*AuthStatusResponse) ProtoMessage()    {}
 func (*AuthStatusResponse) Descriptor() ([]byte, []int) {
-	return fileDescriptor_77a6da22d6a3feb1, []int{80}
+	return fileDescriptor_77a6da22d6a3feb1, []int{81}
 }
 func (m *AuthStatusResponse) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -5401,7 +5452,7 @@ func (m *AuthenticateResponse) Reset()         { *m = AuthenticateResponse{} }
 func (m *AuthenticateResponse) String() string { return proto.CompactTextString(m) }
 func (*AuthenticateResponse) ProtoMessage()    {}
 func (*AuthenticateResponse) Descriptor() ([]byte, []int) {
-	return fileDescriptor_77a6da22d6a3feb1, []int{81}
+	return fileDescriptor_77a6da22d6a3feb1, []int{82}
 }
 func (m *AuthenticateResponse) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -5455,7 +5506,7 @@ func (m *AuthUserAddResponse) Reset()         { *m = AuthUserAddResponse{} }
 func (m *AuthUserAddResponse) String() string { return proto.CompactTextString(m) }
 func (*AuthUserAddResponse) ProtoMessage()    {}
 func (*AuthUserAddResponse) Descriptor() ([]byte, []int) {
-	return fileDescriptor_77a6da22d6a3feb1, []int{82}
+	return fileDescriptor_77a6da22d6a3feb1, []int{83}
 }
 func (m *AuthUserAddResponse) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -5503,7 +5554,7 @@ func (m *AuthUserGetResponse) Reset()         { *m = AuthUserGetResponse{} }
 func (m *AuthUserGetResponse) String() string { return proto.CompactTextString(m) }
 func (*AuthUserGetResponse) ProtoMessage()    {}
 func (*AuthUserGetResponse) Descriptor() ([]byte, []int) {
-	return fileDescriptor_77a6da22d6a3feb1, []int{83}
+	return fileDescriptor_77a6da22d6a3feb1, []int{84}
 }
 func (m *AuthUserGetResponse) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -5557,7 +5608,7 @@ func (m *AuthUserDeleteResponse) Reset()         { *m = AuthUserDeleteResponse{}
 func (m *AuthUserDeleteResponse) String() string { return proto.CompactTextString(m) }
 func (*AuthUserDeleteResponse) ProtoMessage()    {}
 func (*AuthUserDeleteResponse) Descriptor() ([]byte, []int) {
-	return fileDescriptor_77a6da22d6a3feb1, []int{84}
+	return fileDescriptor_77a6da22d6a3feb1, []int{85}
 }
 func (m *AuthUserDeleteResponse) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -5604,7 +5655,7 @@ func (m *AuthUserChangePasswordResponse) Reset()         { *m = AuthUserChangePa
 func (m *AuthUserChangePasswordResponse) String() string { return proto.CompactTextString(m) }
 func (*AuthUserChangePasswordResponse) ProtoMessage()    {}
 func (*AuthUserChangePasswordResponse) Descriptor() ([]byte, []int) {
-	return fileDescriptor_77a6da22d6a3feb1, []int{85}
+	return fileDescriptor_77a6da22d6a3feb1, []int{86}
 }
 func (m *AuthUserChangePasswordResponse) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -5651,7 +5702,7 @@ func (m *AuthUserGrantRoleResponse) Reset()         { *m = AuthUserGrantRoleResp
 func (m *AuthUserGrantRoleResponse) String() string { return proto.CompactTextString(m) }
 func (*AuthUserGrantRoleResponse) ProtoMessage()    {}
 func (*AuthUserGrantRoleResponse) Descriptor() ([]byte, []int) {
-	return fileDescriptor_77a6da22d6a3feb1, []int{86}
+	return fileDescriptor_77a6da22d6a3feb1, []int{87}
 }
 func (m *AuthUserGrantRoleResponse) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -5698,7 +5749,7 @@ func (m *AuthUserRevokeRoleResponse) Reset()         { *m = AuthUserRevokeRoleRe
 func (m *AuthUserRevokeRoleResponse) String() string { return proto.CompactTextString(m) }
 func (*AuthUserRevokeRoleResponse) ProtoMessage()    {}
 func (*AuthUserRevokeRoleResponse) Descriptor() ([]byte, []int) {
-	return fileDescriptor_77a6da22d6a3feb1, []int{87}
+	return fileDescriptor_77a6da22d6a3feb1, []int{88}
 }
 func (m *AuthUserRevokeRoleResponse) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -5745,7 +5796,7 @@ func (m *AuthRoleAddResponse) Reset()         { *m = AuthRoleAddResponse{} }
 func (m *AuthRoleAddResponse) String() string { return proto.CompactTextString(m) }
 func (*AuthRoleAddResponse) ProtoMessage()    {}
 func (*AuthRoleAddResponse) Descriptor() ([]byte, []int) {
-	return fileDescriptor_77a6da22d6a3feb1, []int{88}
+	return fileDescriptor_77a6da22d6a3feb1, []int{89}
 }
 func (m *AuthRoleAddResponse) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -5793,7 +5844,7 @@ func (m *AuthRoleGetResponse) Reset()         { *m = AuthRoleGetResponse{} }
 func (m *AuthRoleGetResponse) String() string { return proto.CompactTextString(m) }
 func (*AuthRoleGetResponse) ProtoMessage()    {}
 func (*AuthRoleGetResponse) Descriptor() ([]byte, []int) {
-	return fileDescriptor_77a6da22d6a3feb1, []int{89}
+	return fileDescriptor_77a6da22d6a3feb1, []int{90}
 }
 func (m *AuthRoleGetResponse) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -5848,7 +5899,7 @@ func (m *AuthRoleListResponse) Reset()         { *m = AuthRoleListResponse{} }
 func (m *AuthRoleListResponse) String() string { return proto.CompactTextString(m) }
 func (*AuthRoleListResponse) ProtoMessage()    {}
 func (*AuthRoleListResponse) Descriptor() ([]byte, []int) {
-	return fileDescriptor_77a6da22d6a3feb1, []int{90}
+	return fileDescriptor_77a6da22d6a3feb1, []int{91}
 }
 func (m *AuthRoleListResponse) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -5903,7 +5954,7 @@ func (m *AuthUserListResponse) Reset()         { *m = AuthUserListResponse{} }
 func (m *AuthUserListResponse) String() string { return proto.CompactTextString(m) }
 func (*AuthUserListResponse) ProtoMessage()    {}
 func (*AuthUserListResponse) Descriptor() ([]byte, []int) {
-	return fileDescriptor_77a6da22d6a3feb1, []int{91}
+	return fileDescriptor_77a6da22d6a3feb1, []int{92}
 }
 func (m *AuthUserListResponse) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -5957,7 +6008,7 @@ func (m *AuthRoleDeleteResponse) Reset()         { *m = AuthRoleDeleteResponse{}
 func (m *AuthRoleDeleteResponse) String() string { return proto.CompactTextString(m) }
 func (*AuthRoleDeleteResponse) ProtoMessage()    {}
 func (*AuthRoleDeleteResponse) Descriptor() ([]byte, []int) {
-	return fileDescriptor_77a6da22d6a3feb1, []int{92}
+	return fileDescriptor_77a6da22d6a3feb1, []int{93}
 }
 func (m *AuthRoleDeleteResponse) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -6004,7 +6055,7 @@ func (m *AuthRoleGrantPermissionResponse) Reset()         { *m = AuthRoleGrantPe
 func (m *AuthRoleGrantPermissionResponse) String() string { return proto.CompactTextString(m) }
 func (*AuthRoleGrantPermissionResponse) ProtoMessage()    {}
 func (*AuthRoleGrantPermissionResponse) Descriptor() ([]byte, []int) {
-	return fileDescriptor_77a6da22d6a3feb1, []int{93}
+	return fileDescriptor_77a6da22d6a3feb1, []int{94}
 }
 func (m *AuthRoleGrantPermissionResponse) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -6051,7 +6102,7 @@ func (m *AuthRoleRevokePermissionResponse) Reset()         { *m = AuthRoleRevoke
 func (m *AuthRoleRevokePermissionResponse) String() string { return proto.CompactTextString(m) }
 func (*AuthRoleRevokePermissionResponse) ProtoMessage()    {}
 func (*AuthRoleRevokePermissionResponse) Descriptor() ([]byte, []int) {
-	return fileDescriptor_77a6da22d6a3feb1, []int{94}
+	return fileDescriptor_77a6da22d6a3feb1, []int{95}
 }
 func (m *AuthRoleRevokePermissionResponse) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -6155,6 +6206,7 @@ func init() {
 	proto.RegisterType((*AlarmResponse)(nil), "etcdserverpb.AlarmResponse")
 	proto.RegisterType((*DowngradeRequest)(nil), "etcdserverpb.DowngradeRequest")
 	proto.RegisterType((*DowngradeResponse)(nil), "etcdserverpb.DowngradeResponse")
+	proto.RegisterType((*DowngradeVersionTestRequest)(nil), "etcdserverpb.DowngradeVersionTestRequest")
 	proto.RegisterType((*StatusRequest)(nil), "etcdserverpb.StatusRequest")
 	proto.RegisterType((*StatusResponse)(nil), "etcdserverpb.StatusResponse")
 	proto.RegisterType((*AuthEnableRequest)(nil), "etcdserverpb.AuthEnableRequest")
@@ -6196,289 +6248,291 @@ func init() {
 func init() { proto.RegisterFile("rpc.proto", fileDescriptor_77a6da22d6a3feb1) }
 
 var fileDescriptor_77a6da22d6a3feb1 = []byte{
-	// 4511 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x3c, 0x5d, 0x6f, 0x1c, 0x47,
-	0x72, 0x9c, 0x5d, 0x92, 0xcb, 0xad, 0x5d, 0x2e, 0x97, 0x2d, 0x4a, 0x5a, 0xad, 0x25, 0x8a, 0x1e,
-	0x59, 0xb6, 0x2c, 0x5b, 0x5c, 0x89, 0x94, 0xec, 0x44, 0x81, 0x9d, 0x5b, 0x91, 0x6b, 0x89, 0x11,
-	0x45, 0xd2, 0xc3, 0x95, 0x7c, 0x56, 0x80, 0x63, 0x86, 0xbb, 0xad, 0xe5, 0x1c, 0x77, 0x67, 0xf6,
-	0x66, 0x86, 0x2b, 0xd2, 0x79, 0x38, 0xe7, 0x92, 0xcb, 0xe1, 0x12, 0xe0, 0x80, 0x38, 0x40, 0x70,
-	0x08, 0x92, 0x97, 0x20, 0x40, 0xf2, 0x90, 0x04, 0xc9, 0x43, 0x1e, 0x82, 0x04, 0xc8, 0x43, 0xf2,
-	0x90, 0x3c, 0x04, 0x08, 0x90, 0x87, 0xbc, 0x26, 0xce, 0x3d, 0xe5, 0x57, 0x1c, 0xfa, 0x6b, 0xba,
-	0x67, 0xa6, 0x87, 0x94, 0x8f, 0x34, 0xfc, 0x62, 0xed, 0x74, 0x55, 0x57, 0x55, 0x57, 0x75, 0x55,
-	0x75, 0x57, 0xb5, 0x09, 0x45, 0x7f, 0xd8, 0x59, 0x1c, 0xfa, 0x5e, 0xe8, 0xa1, 0x32, 0x0e, 0x3b,
-	0xdd, 0x00, 0xfb, 0x23, 0xec, 0x0f, 0x77, 0xeb, 0x73, 0x3d, 0xaf, 0xe7, 0x51, 0x40, 0x83, 0xfc,
+	// 4532 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x3c, 0x4d, 0x6f, 0x1b, 0x49,
+	0x76, 0x6a, 0x52, 0x12, 0xc5, 0x47, 0x8a, 0xa2, 0xca, 0xb2, 0x4d, 0xd3, 0xb6, 0xac, 0x69, 0x8f,
+	0x67, 0x3d, 0x9e, 0xb1, 0x68, 0x4b, 0xf6, 0xcc, 0xc6, 0xc1, 0x4c, 0x96, 0x96, 0x38, 0xb6, 0x62,
+	0x59, 0xd2, 0xb4, 0x68, 0xcf, 0x8e, 0x03, 0xac, 0xd2, 0x22, 0xcb, 0x54, 0xaf, 0xc8, 0x6e, 0x6e,
+	0x77, 0x8b, 0x96, 0x26, 0x87, 0x9d, 0x6c, 0xb2, 0x59, 0x6c, 0x02, 0x2c, 0x90, 0x09, 0x10, 0x2c,
+	0x82, 0xe4, 0x12, 0x04, 0x48, 0x0e, 0x49, 0x90, 0x1c, 0x72, 0x08, 0x12, 0x20, 0x87, 0xe4, 0x90,
+	0x1c, 0x02, 0x04, 0xc8, 0x21, 0xd7, 0x64, 0xb2, 0xa7, 0xfc, 0x8a, 0xa0, 0xbe, 0xba, 0xaa, 0xbb,
+	0xab, 0x25, 0xcf, 0x4a, 0x83, 0xbd, 0x58, 0x5d, 0xf5, 0x5e, 0xbd, 0xf7, 0xea, 0xbd, 0xaa, 0xf7,
+	0xaa, 0xde, 0x2b, 0x13, 0x8a, 0xfe, 0xb0, 0xb3, 0x38, 0xf4, 0xbd, 0xd0, 0x43, 0x65, 0x1c, 0x76,
+	0xba, 0x01, 0xf6, 0x47, 0xd8, 0x1f, 0xee, 0xd6, 0xe7, 0x7a, 0x5e, 0xcf, 0xa3, 0x80, 0x06, 0xf9,
 	0x62, 0x38, 0xf5, 0x1a, 0xc1, 0x69, 0xd8, 0x43, 0xa7, 0x31, 0x18, 0x75, 0x3a, 0xc3, 0xdd, 0xc6,
-	0xfe, 0x88, 0x43, 0xea, 0x11, 0xc4, 0x3e, 0x08, 0xf7, 0x86, 0xbb, 0xf4, 0x1f, 0x0e, 0x5b, 0x88,
-	0x60, 0x23, 0xec, 0x07, 0x8e, 0xe7, 0x0e, 0x77, 0xc5, 0x2f, 0x8e, 0x71, 0xb9, 0xe7, 0x79, 0xbd,
-	0x3e, 0x66, 0xf3, 0x5d, 0xd7, 0x0b, 0xed, 0xd0, 0xf1, 0xdc, 0x80, 0x43, 0xd9, 0x3f, 0x9d, 0x5b,
-	0x3d, 0xec, 0xde, 0xf2, 0x86, 0xd8, 0xb5, 0x87, 0xce, 0x68, 0xa9, 0xe1, 0x0d, 0x29, 0x4e, 0x1a,
-	0xdf, 0xfc, 0x89, 0x01, 0x15, 0x0b, 0x07, 0x43, 0xcf, 0x0d, 0xf0, 0x23, 0x6c, 0x77, 0xb1, 0x8f,
-	0xae, 0x00, 0x74, 0xfa, 0x07, 0x41, 0x88, 0xfd, 0x1d, 0xa7, 0x5b, 0x33, 0x16, 0x8c, 0x1b, 0xe3,
-	0x56, 0x91, 0x8f, 0xac, 0x75, 0xd1, 0x6b, 0x50, 0x1c, 0xe0, 0xc1, 0x2e, 0x83, 0xe6, 0x28, 0x74,
-	0x8a, 0x0d, 0xac, 0x75, 0x51, 0x1d, 0xa6, 0x7c, 0x3c, 0x72, 0x88, 0xb8, 0xb5, 0xfc, 0x82, 0x71,
-	0x23, 0x6f, 0x45, 0xdf, 0x64, 0xa2, 0x6f, 0xbf, 0x08, 0x77, 0x42, 0xec, 0x0f, 0x6a, 0xe3, 0x6c,
-	0x22, 0x19, 0x68, 0x63, 0x7f, 0x70, 0xbf, 0xf0, 0x83, 0xbf, 0xaf, 0xe5, 0x97, 0x17, 0x6f, 0x9b,
-	0xff, 0x32, 0x01, 0x65, 0xcb, 0x76, 0x7b, 0xd8, 0xc2, 0xdf, 0x3b, 0xc0, 0x41, 0x88, 0xaa, 0x90,
-	0xdf, 0xc7, 0x47, 0x54, 0x8e, 0xb2, 0x45, 0x7e, 0x32, 0x42, 0x6e, 0x0f, 0xef, 0x60, 0x97, 0x49,
-	0x50, 0x26, 0x84, 0xdc, 0x1e, 0x6e, 0xb9, 0x5d, 0x34, 0x07, 0x13, 0x7d, 0x67, 0xe0, 0x84, 0x9c,
-	0x3d, 0xfb, 0x88, 0xc9, 0x35, 0x9e, 0x90, 0x6b, 0x05, 0x20, 0xf0, 0xfc, 0x70, 0xc7, 0xf3, 0xbb,
-	0xd8, 0xaf, 0x4d, 0x2c, 0x18, 0x37, 0x2a, 0x4b, 0x6f, 0x2c, 0xaa, 0x16, 0x5e, 0x54, 0x05, 0x5a,
-	0xdc, 0xf6, 0xfc, 0x70, 0x93, 0xe0, 0x5a, 0xc5, 0x40, 0xfc, 0x44, 0x1f, 0x41, 0x89, 0x12, 0x09,
-	0x6d, 0xbf, 0x87, 0xc3, 0xda, 0x24, 0xa5, 0x72, 0xfd, 0x04, 0x2a, 0x6d, 0x8a, 0x6c, 0x51, 0xf6,
-	0xec, 0x37, 0x32, 0xa1, 0x1c, 0x60, 0xdf, 0xb1, 0xfb, 0xce, 0x67, 0xf6, 0x6e, 0x1f, 0xd7, 0x0a,
-	0x0b, 0xc6, 0x8d, 0x29, 0x2b, 0x36, 0x46, 0xd6, 0xbf, 0x8f, 0x8f, 0x82, 0x1d, 0xcf, 0xed, 0x1f,
-	0xd5, 0xa6, 0x28, 0xc2, 0x14, 0x19, 0xd8, 0x74, 0xfb, 0x47, 0xd4, 0x7a, 0xde, 0x81, 0x1b, 0x32,
-	0x68, 0x91, 0x42, 0x8b, 0x74, 0x84, 0x82, 0xef, 0x40, 0x75, 0xe0, 0xb8, 0x3b, 0x03, 0xaf, 0xbb,
-	0x13, 0x29, 0x04, 0x88, 0x42, 0x1e, 0x14, 0x7e, 0x8f, 0x5a, 0xe0, 0x8e, 0x55, 0x19, 0x38, 0xee,
-	0x13, 0xaf, 0x6b, 0x09, 0xfd, 0x90, 0x29, 0xf6, 0x61, 0x7c, 0x4a, 0x29, 0x39, 0xc5, 0x3e, 0x54,
-	0xa7, 0xbc, 0x0f, 0xe7, 0x08, 0x97, 0x8e, 0x8f, 0xed, 0x10, 0xcb, 0x59, 0xe5, 0xf8, 0xac, 0xd9,
-	0x81, 0xe3, 0xae, 0x50, 0x94, 0xd8, 0x44, 0xfb, 0x30, 0x35, 0x71, 0x3a, 0x39, 0xd1, 0x3e, 0x8c,
-	0x4f, 0x34, 0xdf, 0x87, 0x62, 0x64, 0x17, 0x34, 0x05, 0xe3, 0x1b, 0x9b, 0x1b, 0xad, 0xea, 0x18,
-	0x02, 0x98, 0x6c, 0x6e, 0xaf, 0xb4, 0x36, 0x56, 0xab, 0x06, 0x2a, 0x41, 0x61, 0xb5, 0xc5, 0x3e,
-	0x72, 0xf5, 0xc2, 0x17, 0x7c, 0xbf, 0x3d, 0x06, 0x90, 0xa6, 0x40, 0x05, 0xc8, 0x3f, 0x6e, 0x7d,
-	0x5a, 0x1d, 0x23, 0xc8, 0xcf, 0x5a, 0xd6, 0xf6, 0xda, 0xe6, 0x46, 0xd5, 0x20, 0x54, 0x56, 0xac,
-	0x56, 0xb3, 0xdd, 0xaa, 0xe6, 0x08, 0xc6, 0x93, 0xcd, 0xd5, 0x6a, 0x1e, 0x15, 0x61, 0xe2, 0x59,
-	0x73, 0xfd, 0x69, 0xab, 0x3a, 0x1e, 0x11, 0x93, 0xbb, 0xf8, 0x4f, 0x0c, 0x98, 0xe6, 0xe6, 0x66,
-	0xbe, 0x85, 0xee, 0xc2, 0xe4, 0x1e, 0xf5, 0x2f, 0xba, 0x93, 0x4b, 0x4b, 0x97, 0x13, 0x7b, 0x23,
-	0xe6, 0x83, 0x16, 0xc7, 0x45, 0x26, 0xe4, 0xf7, 0x47, 0x41, 0x2d, 0xb7, 0x90, 0xbf, 0x51, 0x5a,
-	0xaa, 0x2e, 0xb2, 0x48, 0xb2, 0xf8, 0x18, 0x1f, 0x3d, 0xb3, 0xfb, 0x07, 0xd8, 0x22, 0x40, 0x84,
-	0x60, 0x7c, 0xe0, 0xf9, 0x98, 0x6e, 0xf8, 0x29, 0x8b, 0xfe, 0x26, 0x5e, 0x40, 0x6d, 0xce, 0x37,
-	0x3b, 0xfb, 0x90, 0xe2, 0xfd, 0x87, 0x01, 0xb0, 0x75, 0x10, 0x66, 0xbb, 0xd8, 0x1c, 0x4c, 0x8c,
-	0x08, 0x07, 0xee, 0x5e, 0xec, 0x83, 0xfa, 0x16, 0xb6, 0x03, 0x1c, 0xf9, 0x16, 0xf9, 0x40, 0x0b,
-	0x50, 0x18, 0xfa, 0x78, 0xb4, 0xb3, 0x3f, 0xa2, 0xdc, 0xa6, 0xa4, 0x9d, 0x26, 0xc9, 0xf8, 0xe3,
-	0x11, 0xba, 0x09, 0x65, 0xa7, 0xe7, 0x7a, 0x3e, 0xde, 0x61, 0x44, 0x27, 0x54, 0xb4, 0x25, 0xab,
-	0xc4, 0x80, 0x74, 0x49, 0x0a, 0x2e, 0x63, 0x35, 0xa9, 0xc5, 0x5d, 0x27, 0x30, 0xb9, 0x9e, 0xcf,
-	0x0d, 0x28, 0xd1, 0xf5, 0x9c, 0x4a, 0xd9, 0x4b, 0x72, 0x21, 0x39, 0x3a, 0x2d, 0xa5, 0xf0, 0xd4,
-	0xd2, 0xa4, 0x08, 0x2e, 0xa0, 0x55, 0xdc, 0xc7, 0x21, 0x3e, 0x4d, 0xf0, 0x52, 0x54, 0x99, 0xd7,
-	0xaa, 0x52, 0xf2, 0xfb, 0x73, 0x03, 0xce, 0xc5, 0x18, 0x9e, 0x6a, 0xe9, 0x35, 0x28, 0x74, 0x29,
-	0x31, 0x26, 0x53, 0xde, 0x12, 0x9f, 0xe8, 0x2e, 0x4c, 0x71, 0x91, 0x82, 0x5a, 0x5e, 0xbf, 0x0d,
-	0xa5, 0x94, 0x05, 0x26, 0x65, 0x20, 0xc5, 0xfc, 0xc7, 0x1c, 0x14, 0xb9, 0x32, 0x36, 0x87, 0xa8,
-	0x09, 0xd3, 0x3e, 0xfb, 0xd8, 0xa1, 0x6b, 0xe6, 0x32, 0xd6, 0xb3, 0xe3, 0xe4, 0xa3, 0x31, 0xab,
-	0xcc, 0xa7, 0xd0, 0x61, 0xf4, 0x2b, 0x50, 0x12, 0x24, 0x86, 0x07, 0x21, 0x37, 0x54, 0x2d, 0x4e,
-	0x40, 0x6e, 0xed, 0x47, 0x63, 0x16, 0x70, 0xf4, 0xad, 0x83, 0x10, 0xb5, 0x61, 0x4e, 0x4c, 0x66,
-	0xeb, 0xe3, 0x62, 0xe4, 0x29, 0x95, 0x85, 0x38, 0x95, 0xb4, 0x39, 0x1f, 0x8d, 0x59, 0x88, 0xcf,
-	0x57, 0x80, 0x68, 0x55, 0x8a, 0x14, 0x1e, 0xb2, 0xfc, 0x92, 0x12, 0xa9, 0x7d, 0xe8, 0x72, 0x22,
-	0x42, 0x5b, 0xcb, 0x8a, 0x6c, 0xed, 0x43, 0x37, 0x52, 0xd9, 0x83, 0x22, 0x14, 0xf8, 0xb0, 0xf9,
-	0xef, 0x39, 0x00, 0x61, 0xb1, 0xcd, 0x21, 0x5a, 0x85, 0x8a, 0xcf, 0xbf, 0x62, 0xfa, 0x7b, 0x4d,
-	0xab, 0x3f, 0x6e, 0xe8, 0x31, 0x6b, 0x5a, 0x4c, 0x62, 0xe2, 0x7e, 0x08, 0xe5, 0x88, 0x8a, 0x54,
-	0xe1, 0x25, 0x8d, 0x0a, 0x23, 0x0a, 0x25, 0x31, 0x81, 0x28, 0xf1, 0x13, 0x38, 0x1f, 0xcd, 0xd7,
-	0x68, 0xf1, 0xf5, 0x63, 0xb4, 0x18, 0x11, 0x3c, 0x27, 0x28, 0xa8, 0x7a, 0x7c, 0xa8, 0x08, 0x26,
-	0x15, 0x79, 0x49, 0xa3, 0x48, 0x86, 0xa4, 0x6a, 0x32, 0x92, 0x30, 0xa6, 0x4a, 0x20, 0x69, 0x9f,
-	0x8d, 0x9b, 0x7f, 0x39, 0x0e, 0x85, 0x15, 0x6f, 0x30, 0xb4, 0x7d, 0xb2, 0x89, 0x26, 0x7d, 0x1c,
-	0x1c, 0xf4, 0x43, 0xaa, 0xc0, 0xca, 0xd2, 0xb5, 0x38, 0x0f, 0x8e, 0x26, 0xfe, 0xb5, 0x28, 0xaa,
-	0xc5, 0xa7, 0x90, 0xc9, 0x3c, 0xcb, 0xe7, 0x5e, 0x61, 0x32, 0xcf, 0xf1, 0x7c, 0x8a, 0x08, 0x08,
-	0x79, 0x19, 0x10, 0xea, 0x50, 0xe0, 0x07, 0x3c, 0x16, 0xac, 0x1f, 0x8d, 0x59, 0x62, 0x00, 0xbd,
-	0x0d, 0x33, 0xc9, 0x54, 0x38, 0xc1, 0x71, 0x2a, 0x9d, 0x78, 0xe6, 0xbc, 0x06, 0xe5, 0x58, 0x86,
-	0x9e, 0xe4, 0x78, 0xa5, 0x81, 0x92, 0x97, 0x2f, 0x88, 0xb0, 0x4e, 0x8e, 0x15, 0xe5, 0x47, 0x63,
-	0x22, 0xb0, 0x5f, 0x15, 0x81, 0x7d, 0x4a, 0x4d, 0xb4, 0x44, 0xaf, 0x3c, 0xc6, 0xbf, 0xa1, 0x46,
-	0xad, 0x6f, 0x91, 0xc9, 0x11, 0x92, 0x0c, 0x5f, 0xa6, 0x05, 0xd3, 0x31, 0x95, 0x91, 0x1c, 0xd9,
-	0xfa, 0xf8, 0x69, 0x73, 0x9d, 0x25, 0xd4, 0x87, 0x34, 0x87, 0x5a, 0x55, 0x83, 0x24, 0xe8, 0xf5,
-	0xd6, 0xf6, 0x76, 0x35, 0x87, 0x2e, 0x40, 0x71, 0x63, 0xb3, 0xbd, 0xc3, 0xb0, 0xf2, 0xf5, 0xc2,
-	0x1f, 0xb3, 0x48, 0x22, 0xf3, 0xf3, 0xa7, 0x11, 0x4d, 0x9e, 0xa2, 0x95, 0xcc, 0x3c, 0xa6, 0x64,
-	0x66, 0x43, 0x64, 0xe6, 0x9c, 0xcc, 0xcc, 0x79, 0x84, 0x60, 0x62, 0xbd, 0xd5, 0xdc, 0xa6, 0x49,
-	0x9a, 0x91, 0x5e, 0x4e, 0x67, 0xeb, 0x07, 0x15, 0x28, 0x33, 0xf3, 0xec, 0x1c, 0xb8, 0xe4, 0x30,
-	0xf1, 0x57, 0x06, 0x80, 0x74, 0x58, 0xd4, 0x80, 0x42, 0x87, 0x89, 0x50, 0x33, 0x68, 0x04, 0x3c,
-	0xaf, 0xb5, 0xb8, 0x25, 0xb0, 0xd0, 0x1d, 0x28, 0x04, 0x07, 0x9d, 0x0e, 0x0e, 0x44, 0xe6, 0xbe,
-	0x98, 0x0c, 0xc2, 0x3c, 0x20, 0x5a, 0x02, 0x8f, 0x4c, 0x79, 0x61, 0x3b, 0xfd, 0x03, 0x9a, 0xc7,
-	0x8f, 0x9f, 0xc2, 0xf1, 0x64, 0x8c, 0xfd, 0x33, 0x03, 0x4a, 0x8a, 0x5b, 0xfc, 0x82, 0x29, 0xe0,
-	0x32, 0x14, 0xa9, 0x30, 0xb8, 0xcb, 0x93, 0xc0, 0x94, 0x25, 0x07, 0xd0, 0x7b, 0x50, 0x14, 0x9e,
-	0x24, 0xf2, 0x40, 0x4d, 0x4f, 0x76, 0x73, 0x68, 0x49, 0x54, 0x29, 0x64, 0x1b, 0x66, 0xa9, 0x9e,
-	0x3a, 0xe4, 0xf6, 0x21, 0x34, 0xab, 0x1e, 0xcb, 0x8d, 0xc4, 0xb1, 0xbc, 0x0e, 0x53, 0xc3, 0xbd,
-	0xa3, 0xc0, 0xe9, 0xd8, 0x7d, 0x2e, 0x4e, 0xf4, 0x2d, 0xa9, 0x6e, 0x03, 0x52, 0xa9, 0x9e, 0x46,
-	0x01, 0x92, 0xe8, 0x05, 0x28, 0x3d, 0xb2, 0x83, 0x3d, 0x2e, 0xa4, 0x1c, 0xbf, 0x0b, 0xd3, 0x64,
-	0xfc, 0xf1, 0xb3, 0x57, 0x10, 0x5f, 0xcc, 0x5a, 0x36, 0xff, 0xc9, 0x80, 0x8a, 0x98, 0x76, 0x2a,
-	0x03, 0x21, 0x18, 0xdf, 0xb3, 0x83, 0x3d, 0xaa, 0x8c, 0x69, 0x8b, 0xfe, 0x46, 0x6f, 0x43, 0xb5,
-	0xc3, 0xd6, 0xbf, 0x93, 0xb8, 0x77, 0xcd, 0xf0, 0xf1, 0xc8, 0xf7, 0xdf, 0x85, 0x69, 0x32, 0x65,
-	0x27, 0x7e, 0x0f, 0x12, 0x6e, 0xfc, 0x9e, 0x55, 0xde, 0xa3, 0x6b, 0x4e, 0x8a, 0x6f, 0x43, 0x99,
-	0x29, 0xe3, 0xac, 0x65, 0x97, 0x7a, 0xad, 0xc3, 0xcc, 0xb6, 0x6b, 0x0f, 0x83, 0x3d, 0x2f, 0x4c,
-	0xe8, 0x7c, 0xd9, 0xfc, 0x3b, 0x03, 0xaa, 0x12, 0x78, 0x2a, 0x19, 0xde, 0x82, 0x19, 0x1f, 0x0f,
-	0x6c, 0xc7, 0x75, 0xdc, 0xde, 0xce, 0xee, 0x51, 0x88, 0x03, 0x7e, 0x7d, 0xad, 0x44, 0xc3, 0x0f,
-	0xc8, 0x28, 0x11, 0x76, 0xb7, 0xef, 0xed, 0xf2, 0x20, 0x4d, 0x7f, 0xa3, 0xd7, 0xe3, 0x51, 0xba,
-	0x28, 0xf5, 0x26, 0xc6, 0xa5, 0xcc, 0x3f, 0xcd, 0x41, 0xf9, 0x13, 0x3b, 0xec, 0x88, 0x1d, 0x84,
-	0xd6, 0xa0, 0x12, 0x85, 0x71, 0x3a, 0xc2, 0xe5, 0x4e, 0x1c, 0x38, 0xe8, 0x1c, 0x71, 0xaf, 0x11,
-	0x07, 0x8e, 0xe9, 0x8e, 0x3a, 0x40, 0x49, 0xd9, 0x6e, 0x07, 0xf7, 0x23, 0x52, 0xb9, 0x6c, 0x52,
-	0x14, 0x51, 0x25, 0xa5, 0x0e, 0xa0, 0x6f, 0x43, 0x75, 0xe8, 0x7b, 0x3d, 0x1f, 0x07, 0x41, 0x44,
-	0x8c, 0xa5, 0x70, 0x53, 0x43, 0x6c, 0x8b, 0xa3, 0x26, 0x4e, 0x31, 0x77, 0x1f, 0x8d, 0x59, 0x33,
-	0xc3, 0x38, 0x4c, 0x06, 0xd6, 0x19, 0x79, 0xde, 0x63, 0x91, 0xf5, 0x47, 0x79, 0x40, 0xe9, 0x65,
-	0x7e, 0xd5, 0x63, 0xf2, 0x75, 0xa8, 0x04, 0xa1, 0xed, 0xa7, 0xf6, 0xfc, 0x34, 0x1d, 0x8d, 0x76,
-	0xfc, 0x5b, 0x10, 0x49, 0xb6, 0xe3, 0x7a, 0xa1, 0xf3, 0xe2, 0x88, 0x5d, 0x50, 0xac, 0x8a, 0x18,
-	0xde, 0xa0, 0xa3, 0x68, 0x03, 0x0a, 0x2f, 0x9c, 0x7e, 0x88, 0xfd, 0xa0, 0x36, 0xb1, 0x90, 0xbf,
-	0x51, 0x59, 0x7a, 0xe7, 0x24, 0xc3, 0x2c, 0x7e, 0x44, 0xf1, 0xdb, 0x47, 0x43, 0xf5, 0xf4, 0xcb,
-	0x89, 0xa8, 0xc7, 0xf8, 0x49, 0xfd, 0x8d, 0xc8, 0x84, 0xa9, 0x97, 0x84, 0xe8, 0x8e, 0xd3, 0xa5,
-	0xb9, 0x38, 0xf2, 0xc3, 0xbb, 0x56, 0x81, 0x02, 0xd6, 0xba, 0xe8, 0x1a, 0x4c, 0xbd, 0xf0, 0xed,
-	0xde, 0x00, 0xbb, 0x21, 0xbb, 0xe5, 0x4b, 0x9c, 0x08, 0x60, 0x2e, 0x02, 0x48, 0x51, 0x48, 0xe6,
-	0xdb, 0xd8, 0xdc, 0x7a, 0xda, 0xae, 0x8e, 0xa1, 0x32, 0x4c, 0x6d, 0x6c, 0xae, 0xb6, 0xd6, 0x5b,
-	0x24, 0x37, 0x8a, 0x9c, 0x77, 0x47, 0x3a, 0x5d, 0x53, 0x18, 0x22, 0xb6, 0x27, 0x54, 0xb9, 0x8c,
-	0xf8, 0xa5, 0x5b, 0xc8, 0x25, 0x48, 0xdc, 0x31, 0xaf, 0xc2, 0x9c, 0x6e, 0x6b, 0x08, 0x84, 0xbb,
-	0xe6, 0xbf, 0xe6, 0x60, 0x9a, 0x3b, 0xc2, 0xa9, 0x3c, 0xf7, 0x92, 0x22, 0x15, 0xbf, 0x9e, 0x08,
-	0x25, 0xd5, 0xa0, 0xc0, 0x1c, 0xa4, 0xcb, 0xef, 0xbf, 0xe2, 0x93, 0x04, 0x67, 0xb6, 0xdf, 0x71,
-	0x97, 0x9b, 0x3d, 0xfa, 0xd6, 0x86, 0xcd, 0x89, 0xcc, 0xb0, 0x19, 0x39, 0x9c, 0x1d, 0xf0, 0x83,
-	0x55, 0x51, 0x9a, 0xa2, 0x2c, 0x9c, 0x8a, 0x00, 0x63, 0x36, 0x2b, 0x64, 0xd8, 0x0c, 0x5d, 0x87,
-	0x49, 0x3c, 0xc2, 0x6e, 0x18, 0xd4, 0x4a, 0x34, 0x91, 0x4e, 0x8b, 0x0b, 0x55, 0x8b, 0x8c, 0x5a,
-	0x1c, 0x28, 0x4d, 0xf5, 0x21, 0xcc, 0xd2, 0xfb, 0xee, 0x43, 0xdf, 0x76, 0xd5, 0x3b, 0x7b, 0xbb,
-	0xbd, 0xce, 0xd3, 0x0e, 0xf9, 0x89, 0x2a, 0x90, 0x5b, 0x5b, 0xe5, 0xfa, 0xc9, 0xad, 0xad, 0xca,
-	0xf9, 0xbf, 0x6f, 0x00, 0x52, 0x09, 0x9c, 0xca, 0x16, 0x09, 0x2e, 0x42, 0x8e, 0xbc, 0x94, 0x63,
-	0x0e, 0x26, 0xb0, 0xef, 0x7b, 0x3e, 0x0b, 0x94, 0x16, 0xfb, 0x90, 0xd2, 0xdc, 0xe2, 0xc2, 0x58,
-	0x78, 0xe4, 0xed, 0x47, 0x11, 0x80, 0x91, 0x35, 0xd2, 0xc2, 0xb7, 0xe1, 0x5c, 0x0c, 0xfd, 0x6c,
-	0x52, 0xfc, 0x26, 0xcc, 0x50, 0xaa, 0x2b, 0x7b, 0xb8, 0xb3, 0x3f, 0xf4, 0x1c, 0x37, 0x25, 0x01,
-	0xba, 0x46, 0x62, 0x97, 0x48, 0x17, 0x64, 0x89, 0x6c, 0xcd, 0xe5, 0x68, 0xb0, 0xdd, 0x5e, 0x97,
-	0x5b, 0x7d, 0x17, 0x2e, 0x24, 0x08, 0x8a, 0x95, 0xfd, 0x2a, 0x94, 0x3a, 0xd1, 0x60, 0xc0, 0x4f,
-	0x90, 0x57, 0xe2, 0xe2, 0x26, 0xa7, 0xaa, 0x33, 0x24, 0x8f, 0x6f, 0xc3, 0xc5, 0x14, 0x8f, 0xb3,
-	0x50, 0xc7, 0x5d, 0xf3, 0x36, 0x9c, 0xa7, 0x94, 0x1f, 0x63, 0x3c, 0x6c, 0xf6, 0x9d, 0xd1, 0xc9,
-	0x66, 0x39, 0xe2, 0xeb, 0x55, 0x66, 0x7c, 0xbd, 0xdb, 0x4a, 0xb2, 0x6e, 0x71, 0xd6, 0x6d, 0x67,
-	0x80, 0xdb, 0xde, 0x7a, 0xb6, 0xb4, 0x24, 0x91, 0xef, 0xe3, 0xa3, 0x80, 0x1f, 0x1f, 0xe9, 0x6f,
-	0x19, 0xbd, 0xfe, 0xc6, 0xe0, 0xea, 0x54, 0xe9, 0x7c, 0xcd, 0xae, 0x31, 0x0f, 0xd0, 0x23, 0x3e,
-	0x88, 0xbb, 0x04, 0xc0, 0x6a, 0x73, 0xca, 0x48, 0x24, 0x30, 0xc9, 0x42, 0xe5, 0xa4, 0xc0, 0x57,
-	0xb8, 0xe3, 0xd0, 0xff, 0x04, 0xa9, 0x93, 0xd2, 0x9b, 0x50, 0xa2, 0x90, 0xed, 0xd0, 0x0e, 0x0f,
-	0x82, 0x2c, 0xcb, 0x2d, 0x9b, 0x3f, 0x32, 0xb8, 0x47, 0x09, 0x3a, 0xa7, 0x5a, 0xf3, 0x1d, 0x98,
-	0xa4, 0x37, 0x44, 0x71, 0xd3, 0xb9, 0xa4, 0xd9, 0xd8, 0x4c, 0x22, 0x8b, 0x23, 0x2a, 0xe7, 0x24,
-	0x03, 0x26, 0x9f, 0xd0, 0xce, 0x81, 0x22, 0xed, 0xb8, 0xb0, 0x9c, 0x6b, 0x0f, 0x58, 0xf9, 0xb1,
-	0x68, 0xd1, 0xdf, 0xf4, 0x42, 0x80, 0xb1, 0xff, 0xd4, 0x5a, 0x67, 0x37, 0x90, 0xa2, 0x15, 0x7d,
-	0x13, 0xc5, 0x76, 0xfa, 0x0e, 0x76, 0x43, 0x0a, 0x1d, 0xa7, 0x50, 0x65, 0x04, 0x5d, 0x87, 0xa2,
-	0x13, 0xac, 0x63, 0xdb, 0x77, 0x79, 0x89, 0x5f, 0x09, 0xcc, 0x12, 0x22, 0xf7, 0xd8, 0x77, 0xa0,
-	0xca, 0x24, 0x6b, 0x76, 0xbb, 0xca, 0x69, 0x3f, 0xe2, 0x6f, 0x24, 0xf8, 0xc7, 0xe8, 0xe7, 0x4e,
-	0xa6, 0xff, 0xb7, 0x06, 0xcc, 0x2a, 0x0c, 0x4e, 0x65, 0x82, 0x77, 0x61, 0x92, 0xf5, 0x5f, 0xf8,
-	0x51, 0x70, 0x2e, 0x3e, 0x8b, 0xb1, 0xb1, 0x38, 0x0e, 0x5a, 0x84, 0x02, 0xfb, 0x25, 0xae, 0x71,
-	0x7a, 0x74, 0x81, 0x24, 0x45, 0x5e, 0x84, 0x73, 0x1c, 0x86, 0x07, 0x9e, 0xce, 0xe7, 0xc6, 0xe3,
-	0x11, 0xe2, 0x87, 0x06, 0xcc, 0xc5, 0x27, 0x9c, 0x6a, 0x95, 0x8a, 0xdc, 0xb9, 0xaf, 0x24, 0xf7,
-	0xaf, 0x09, 0xb9, 0x9f, 0x0e, 0xbb, 0xca, 0x91, 0x33, 0xb9, 0xe3, 0x54, 0xeb, 0xe6, 0xe2, 0xd6,
-	0x95, 0xb4, 0x7e, 0x12, 0xad, 0x49, 0x10, 0x3b, 0xd5, 0x9a, 0xde, 0x7f, 0xa5, 0x35, 0x29, 0x47,
-	0xb0, 0xd4, 0xe2, 0xd6, 0xc4, 0x36, 0x5a, 0x77, 0x82, 0x28, 0xe3, 0xbc, 0x03, 0xe5, 0xbe, 0xe3,
-	0x62, 0xdb, 0xe7, 0x3d, 0x24, 0x43, 0xdd, 0x8f, 0xf7, 0xac, 0x18, 0x50, 0x92, 0xfa, 0x6d, 0x03,
-	0x90, 0x4a, 0xeb, 0x9b, 0xb1, 0x56, 0x43, 0x28, 0x78, 0xcb, 0xf7, 0x06, 0x5e, 0x78, 0xd2, 0x36,
-	0xbb, 0x6b, 0xfe, 0xae, 0x01, 0xe7, 0x13, 0x33, 0xbe, 0x09, 0xc9, 0xef, 0x9a, 0x97, 0x61, 0x76,
-	0x15, 0x8b, 0x33, 0x5e, 0xaa, 0x76, 0xb0, 0x0d, 0x48, 0x85, 0x9e, 0xcd, 0x29, 0xe6, 0x97, 0x60,
-	0xf6, 0x89, 0x37, 0x22, 0x81, 0x9c, 0x80, 0x65, 0x98, 0x62, 0xc5, 0xac, 0x48, 0x5f, 0xd1, 0xb7,
-	0x0c, 0xbd, 0xdb, 0x80, 0xd4, 0x99, 0x67, 0x21, 0xce, 0xb2, 0xf9, 0xbf, 0x06, 0x94, 0x9b, 0x7d,
-	0xdb, 0x1f, 0x08, 0x51, 0x3e, 0x84, 0x49, 0x56, 0x99, 0xe1, 0x65, 0xd6, 0x37, 0xe3, 0xf4, 0x54,
-	0x5c, 0xf6, 0xd1, 0x64, 0x75, 0x1c, 0x3e, 0x8b, 0x2c, 0x85, 0x77, 0x96, 0x57, 0x13, 0x9d, 0xe6,
-	0x55, 0x74, 0x0b, 0x26, 0x6c, 0x32, 0x85, 0xa6, 0xd7, 0x4a, 0xb2, 0x5c, 0x46, 0xa9, 0x91, 0x2b,
-	0x91, 0xc5, 0xb0, 0xcc, 0x0f, 0xa0, 0xa4, 0x70, 0x40, 0x05, 0xc8, 0x3f, 0x6c, 0xf1, 0x6b, 0x52,
-	0x73, 0xa5, 0xbd, 0xf6, 0x8c, 0x95, 0x10, 0x2b, 0x00, 0xab, 0xad, 0xe8, 0x3b, 0xa7, 0x69, 0xec,
-	0xd9, 0x9c, 0x0e, 0xcf, 0x5b, 0xaa, 0x84, 0x46, 0x96, 0x84, 0xb9, 0x57, 0x91, 0x50, 0xb2, 0xf8,
-	0x2d, 0x03, 0xa6, 0xb9, 0x6a, 0x4e, 0x9b, 0x9a, 0x29, 0xe5, 0x8c, 0xd4, 0xac, 0x2c, 0xc3, 0xe2,
-	0x88, 0x52, 0x86, 0x7f, 0x36, 0xa0, 0xba, 0xea, 0xbd, 0x74, 0x7b, 0xbe, 0xdd, 0x8d, 0x7c, 0xf0,
-	0xa3, 0x84, 0x39, 0x17, 0x13, 0x95, 0xfe, 0x04, 0xbe, 0x1c, 0x48, 0x98, 0xb5, 0x26, 0x6b, 0x29,
-	0x2c, 0xbf, 0x8b, 0x4f, 0xf3, 0x5b, 0x30, 0x93, 0x98, 0x44, 0x0c, 0xf4, 0xac, 0xb9, 0xbe, 0xb6,
-	0x4a, 0x0c, 0x42, 0xeb, 0xbd, 0xad, 0x8d, 0xe6, 0x83, 0xf5, 0x16, 0xef, 0xca, 0x36, 0x37, 0x56,
-	0x5a, 0xeb, 0xd2, 0x50, 0xf7, 0xc4, 0x0a, 0xee, 0x99, 0x7d, 0x98, 0x55, 0x04, 0x3a, 0x6d, 0x73,
-	0x4c, 0x2f, 0xaf, 0xe4, 0x56, 0x83, 0x69, 0x7e, 0xca, 0x49, 0x3a, 0xfe, 0x7f, 0xe7, 0xa1, 0x22,
-	0x40, 0x5f, 0x8f, 0x14, 0xe8, 0x02, 0x4c, 0x76, 0x77, 0xb7, 0x9d, 0xcf, 0x44, 0x5f, 0x96, 0x7f,
-	0x91, 0xf1, 0x3e, 0xe3, 0xc3, 0x5e, 0x5b, 0xf0, 0x2f, 0x74, 0x99, 0x3d, 0xc4, 0x58, 0x73, 0xbb,
-	0xf8, 0x90, 0x1e, 0x86, 0xc6, 0x2d, 0x39, 0x40, 0x8b, 0x9a, 0xfc, 0x55, 0x06, 0xbd, 0xeb, 0x2a,
-	0xaf, 0x34, 0xd0, 0x32, 0x54, 0xc9, 0xef, 0xe6, 0x70, 0xd8, 0x77, 0x70, 0x97, 0x11, 0x20, 0xd7,
-	0xdc, 0x71, 0x79, 0xda, 0x49, 0x21, 0xa0, 0xab, 0x30, 0x49, 0xaf, 0x80, 0x41, 0x6d, 0x8a, 0xe4,
-	0x55, 0x89, 0xca, 0x87, 0xd1, 0xdb, 0x50, 0x62, 0x12, 0xaf, 0xb9, 0x4f, 0x03, 0x4c, 0xdf, 0x2c,
-	0x28, 0xf5, 0x10, 0x15, 0x16, 0x3f, 0x67, 0x41, 0xd6, 0x39, 0x0b, 0x35, 0xa0, 0x12, 0x84, 0x9e,
-	0x6f, 0xf7, 0xf0, 0x33, 0xae, 0xb2, 0x52, 0xbc, 0x68, 0x97, 0x00, 0x4b, 0x11, 0x3e, 0x3e, 0xf0,
-	0x42, 0x3b, 0xfe, 0x50, 0xe1, 0x3d, 0x4b, 0x85, 0x49, 0xcb, 0x5e, 0x86, 0xd9, 0xe6, 0x41, 0xb8,
-	0xd7, 0x72, 0x49, 0x1e, 0x4d, 0xd9, 0xfd, 0x0a, 0x20, 0x02, 0x5d, 0x75, 0x02, 0x2d, 0x98, 0x4f,
-	0xd6, 0x6e, 0x9a, 0x7b, 0xe6, 0x06, 0x9c, 0x23, 0x50, 0xec, 0x86, 0x4e, 0x47, 0x39, 0xb3, 0x88,
-	0x53, 0xb1, 0x91, 0x38, 0x15, 0xdb, 0x41, 0xf0, 0xd2, 0xf3, 0xbb, 0x7c, 0x5f, 0x44, 0xdf, 0x92,
-	0xdb, 0x3f, 0x18, 0x4c, 0x9a, 0xa7, 0x41, 0xec, 0x44, 0xfb, 0x15, 0xe9, 0xa1, 0x5f, 0x86, 0x02,
-	0x7f, 0x49, 0xc4, 0x0b, 0x85, 0x17, 0x16, 0xd9, 0x0b, 0xa6, 0x45, 0x4e, 0x78, 0x93, 0x41, 0x95,
-	0x62, 0x16, 0xc7, 0x27, 0x16, 0xd9, 0xb3, 0x83, 0x3d, 0xdc, 0xdd, 0x12, 0xc4, 0x63, 0x65, 0xd4,
-	0x7b, 0x56, 0x02, 0x2c, 0x65, 0xbf, 0x23, 0x45, 0x7f, 0x88, 0xc3, 0x63, 0x44, 0x57, 0x0b, 0xf5,
-	0xe7, 0xc5, 0x14, 0xde, 0x5f, 0x7c, 0x95, 0x59, 0x3f, 0x36, 0xe0, 0x8a, 0x98, 0xb6, 0xb2, 0x67,
-	0xbb, 0x3d, 0x2c, 0x84, 0xf9, 0x45, 0xf5, 0x95, 0x5e, 0x74, 0xfe, 0x15, 0x17, 0xfd, 0x18, 0x6a,
-	0xd1, 0xa2, 0x69, 0xd1, 0xc6, 0xeb, 0xab, 0x8b, 0x38, 0x08, 0x78, 0xf0, 0x28, 0x5a, 0xf4, 0x37,
-	0x19, 0xf3, 0xbd, 0x7e, 0x74, 0x5f, 0x22, 0xbf, 0x25, 0xb1, 0x75, 0xb8, 0x24, 0x88, 0xf1, 0x2a,
-	0x4a, 0x9c, 0x5a, 0x6a, 0x4d, 0xc7, 0x52, 0xe3, 0xf6, 0x20, 0x34, 0x8e, 0xdf, 0x4a, 0xda, 0x29,
-	0x71, 0x13, 0x52, 0x2e, 0x86, 0x8e, 0xcb, 0x3c, 0xf3, 0x00, 0x22, 0xb3, 0x72, 0xb4, 0x4d, 0xc1,
-	0x09, 0x49, 0x2d, 0x9c, 0x6f, 0x01, 0x02, 0x4f, 0x6d, 0x81, 0x6c, 0xae, 0x18, 0xe6, 0x23, 0x41,
-	0x89, 0xda, 0xb7, 0xb0, 0x3f, 0x70, 0x82, 0x40, 0xe9, 0x58, 0xe9, 0xd4, 0xf5, 0x26, 0x8c, 0x0f,
-	0x31, 0xcf, 0xf3, 0xa5, 0x25, 0x24, 0x7c, 0x42, 0x99, 0x4c, 0xe1, 0x92, 0xcd, 0x00, 0xae, 0x0a,
-	0x36, 0xcc, 0x20, 0x5a, 0x3e, 0x49, 0x31, 0x45, 0x95, 0x3c, 0x97, 0x51, 0x25, 0xcf, 0xc7, 0xab,
-	0xe4, 0xb1, 0xb3, 0xa7, 0x1a, 0xa8, 0xce, 0xe6, 0xec, 0xd9, 0x66, 0x06, 0x88, 0xe2, 0xdb, 0xd9,
-	0x50, 0xfd, 0x03, 0x1e, 0xa8, 0xce, 0x2a, 0x63, 0x62, 0xba, 0x66, 0xd1, 0xcf, 0x14, 0x9f, 0xc8,
-	0x84, 0x32, 0x31, 0x92, 0xa5, 0xb6, 0x0f, 0xc6, 0xad, 0xd8, 0x98, 0x0c, 0xc6, 0xfb, 0x30, 0x17,
-	0x0f, 0xc6, 0xa7, 0x12, 0x6a, 0x0e, 0x26, 0x42, 0x6f, 0x1f, 0x8b, 0x24, 0xce, 0x3e, 0x52, 0x6a,
-	0x8d, 0x02, 0xf5, 0xd9, 0xa8, 0xf5, 0xbb, 0x92, 0x2a, 0x75, 0xc0, 0xd3, 0xae, 0x80, 0x6c, 0x47,
-	0x71, 0x4d, 0x66, 0x1f, 0x92, 0xd7, 0x27, 0x70, 0x21, 0x19, 0x7c, 0xcf, 0x66, 0x11, 0x3b, 0xcc,
-	0x39, 0x75, 0xe1, 0xf9, 0x6c, 0x18, 0x3c, 0x97, 0x71, 0x52, 0x09, 0xba, 0x67, 0x43, 0xfb, 0xd7,
-	0xa1, 0xae, 0x8b, 0xc1, 0x67, 0xea, 0x8b, 0x51, 0x48, 0x3e, 0x1b, 0xaa, 0x3f, 0x34, 0x24, 0x59,
-	0x75, 0xd7, 0x7c, 0xf0, 0x55, 0xc8, 0x8a, 0x5c, 0x77, 0x3b, 0xda, 0x3e, 0x8d, 0x28, 0x5a, 0xe6,
-	0xf5, 0xd1, 0x52, 0x4e, 0xa1, 0x88, 0xc2, 0xff, 0x64, 0xa8, 0xff, 0x3a, 0x77, 0x2f, 0x67, 0x26,
-	0xf3, 0xce, 0x69, 0x99, 0x91, 0xf4, 0x1c, 0x31, 0xa3, 0x1f, 0x29, 0x57, 0x51, 0x93, 0xd4, 0xd9,
-	0x98, 0xee, 0x37, 0x64, 0x82, 0x49, 0xe5, 0xb1, 0xb3, 0xe1, 0x60, 0xc3, 0x42, 0x76, 0x0a, 0x3b,
-	0x13, 0x16, 0x37, 0x9b, 0x50, 0x8c, 0x2e, 0xc9, 0xca, 0x93, 0xde, 0x12, 0x14, 0x36, 0x36, 0xb7,
-	0xb7, 0x9a, 0x2b, 0xe4, 0x0e, 0x38, 0x07, 0x85, 0x95, 0x4d, 0xcb, 0x7a, 0xba, 0xd5, 0x26, 0x97,
-	0xc0, 0xe4, 0x0b, 0x9f, 0xa5, 0x9f, 0xe5, 0x21, 0xf7, 0xf8, 0x19, 0xfa, 0x14, 0x26, 0xd8, 0x0b,
-	0xb3, 0x63, 0x1e, 0x1a, 0xd6, 0x8f, 0x7b, 0x44, 0x67, 0x5e, 0xfc, 0xc1, 0x7f, 0xfd, 0xec, 0x0f,
-	0x73, 0xb3, 0x66, 0xb9, 0x31, 0x5a, 0x6e, 0xec, 0x8f, 0x1a, 0x34, 0xc9, 0xde, 0x37, 0x6e, 0xa2,
-	0x8f, 0x21, 0xbf, 0x75, 0x10, 0xa2, 0xcc, 0x07, 0x88, 0xf5, 0xec, 0x77, 0x75, 0xe6, 0x79, 0x4a,
-	0x74, 0xc6, 0x04, 0x4e, 0x74, 0x78, 0x10, 0x12, 0x92, 0xdf, 0x83, 0x92, 0xfa, 0x2a, 0xee, 0xc4,
-	0x57, 0x89, 0xf5, 0x93, 0x5f, 0xdc, 0x99, 0x57, 0x28, 0xab, 0x8b, 0x26, 0xe2, 0xac, 0xd8, 0xbb,
-	0x3d, 0x75, 0x15, 0xed, 0x43, 0x17, 0x65, 0xbe, 0x59, 0xac, 0x67, 0x3f, 0xc2, 0x4b, 0xad, 0x22,
-	0x3c, 0x74, 0x09, 0xc9, 0xef, 0xf2, 0xd7, 0x76, 0x9d, 0x10, 0x5d, 0xd5, 0x3c, 0x97, 0x52, 0x9f,
-	0x01, 0xd5, 0x17, 0xb2, 0x11, 0x38, 0x93, 0xcb, 0x94, 0xc9, 0x05, 0x73, 0x96, 0x33, 0xe9, 0x44,
-	0x28, 0xf7, 0x8d, 0x9b, 0x4b, 0x1d, 0x98, 0xa0, 0x6d, 0x66, 0xf4, 0x5c, 0xfc, 0xa8, 0x6b, 0x1a,
-	0xf8, 0x19, 0x86, 0x8e, 0x35, 0xa8, 0xcd, 0x39, 0xca, 0xa8, 0x62, 0x16, 0x09, 0x23, 0xda, 0x64,
-	0xbe, 0x6f, 0xdc, 0xbc, 0x61, 0xdc, 0x36, 0x96, 0xfe, 0x7a, 0x02, 0x26, 0x68, 0x3b, 0x03, 0xed,
-	0x03, 0xc8, 0x76, 0x6a, 0x72, 0x75, 0xa9, 0x4e, 0x6d, 0x72, 0x75, 0xe9, 0x4e, 0xac, 0x59, 0xa7,
-	0x4c, 0xe7, 0xcc, 0x19, 0xc2, 0x94, 0x76, 0x49, 0x1a, 0xb4, 0x29, 0x44, 0xf4, 0xf8, 0x63, 0x83,
-	0xf7, 0x75, 0x98, 0x9b, 0x21, 0x1d, 0xb5, 0x58, 0x2b, 0x35, 0xb9, 0x1d, 0x34, 0xdd, 0x53, 0xf3,
-	0x1e, 0x65, 0xd8, 0x30, 0xab, 0x92, 0xa1, 0x4f, 0x31, 0xee, 0x1b, 0x37, 0x9f, 0xd7, 0xcc, 0x73,
-	0x5c, 0xcb, 0x09, 0x08, 0xfa, 0x3e, 0x54, 0xe2, 0x4d, 0x3f, 0x74, 0x4d, 0xc3, 0x2b, 0xd9, 0x44,
-	0xac, 0xbf, 0x71, 0x3c, 0x12, 0x97, 0x69, 0x9e, 0xca, 0xc4, 0x99, 0x33, 0xce, 0xfb, 0x18, 0x0f,
-	0x6d, 0x82, 0xc4, 0x6d, 0x80, 0xfe, 0xd4, 0xe0, 0x7d, 0x5b, 0xd9, 0xb3, 0x43, 0x3a, 0xea, 0xa9,
-	0xd6, 0x60, 0xfd, 0xfa, 0x09, 0x58, 0x5c, 0x88, 0x0f, 0xa8, 0x10, 0xef, 0x9b, 0x73, 0x52, 0x88,
-	0xd0, 0x19, 0xe0, 0xd0, 0xe3, 0x52, 0x3c, 0xbf, 0x6c, 0x5e, 0x8c, 0x29, 0x27, 0x06, 0x95, 0xc6,
-	0x62, 0xbd, 0x35, 0xad, 0xb1, 0x62, 0xed, 0x3b, 0xad, 0xb1, 0xe2, 0x8d, 0x39, 0x9d, 0xb1, 0x78,
-	0x27, 0x4d, 0x63, 0xac, 0x08, 0xb2, 0xf4, 0xff, 0xe3, 0x50, 0x58, 0x61, 0xff, 0xd7, 0x0e, 0xf2,
-	0xa0, 0x18, 0x75, 0x9b, 0xd0, 0xbc, 0xae, 0xa0, 0x2d, 0xaf, 0x72, 0xf5, 0xab, 0x99, 0x70, 0x2e,
-	0xd0, 0xeb, 0x54, 0xa0, 0xd7, 0xcc, 0x0b, 0x84, 0x33, 0xff, 0x1f, 0x83, 0x1a, 0xac, 0xec, 0xd9,
-	0xb0, 0xbb, 0x5d, 0xa2, 0x88, 0xdf, 0x84, 0xb2, 0xda, 0xfb, 0x41, 0xaf, 0x6b, 0x8b, 0xe8, 0x6a,
-	0x23, 0xa9, 0x6e, 0x1e, 0x87, 0xc2, 0x39, 0xbf, 0x41, 0x39, 0xcf, 0x9b, 0x97, 0x34, 0x9c, 0x7d,
-	0x8a, 0x1a, 0x63, 0xce, 0x9a, 0x34, 0x7a, 0xe6, 0xb1, 0x6e, 0x90, 0x9e, 0x79, 0xbc, 0xc7, 0x73,
-	0x2c, 0xf3, 0x03, 0x8a, 0x4a, 0x98, 0x07, 0x00, 0xb2, 0x8b, 0x82, 0xb4, 0xba, 0x54, 0x2e, 0xac,
-	0xc9, 0xe0, 0x90, 0x6e, 0xc0, 0x98, 0x26, 0x65, 0xcb, 0xf7, 0x5d, 0x82, 0x6d, 0xdf, 0x09, 0x42,
-	0xe6, 0x98, 0xd3, 0xb1, 0x1e, 0x08, 0xd2, 0xae, 0x27, 0xde, 0x52, 0xa9, 0x5f, 0x3b, 0x16, 0x87,
-	0x73, 0xbf, 0x4e, 0xb9, 0x5f, 0x35, 0xeb, 0x1a, 0xee, 0x43, 0x86, 0x4b, 0x36, 0xdb, 0xe7, 0x05,
-	0x28, 0x3d, 0xb1, 0x1d, 0x37, 0xc4, 0xae, 0xed, 0x76, 0x30, 0xda, 0x85, 0x09, 0x9a, 0xbb, 0x93,
-	0x81, 0x58, 0x2d, 0xf9, 0x27, 0x03, 0x71, 0xac, 0xe6, 0x6d, 0x2e, 0x50, 0xc6, 0x75, 0xf3, 0x3c,
-	0x61, 0x3c, 0x90, 0xa4, 0x1b, 0xac, 0x5a, 0x6e, 0xdc, 0x44, 0x2f, 0x60, 0x92, 0xf7, 0xba, 0x13,
-	0x84, 0x62, 0x45, 0xb5, 0xfa, 0x65, 0x3d, 0x50, 0xb7, 0x97, 0x55, 0x36, 0x01, 0xc5, 0x23, 0x7c,
-	0x46, 0x00, 0xb2, 0x75, 0x93, 0xb4, 0x68, 0xaa, 0xe5, 0x53, 0x5f, 0xc8, 0x46, 0xd0, 0xe9, 0x54,
-	0xe5, 0xd9, 0x8d, 0x70, 0x09, 0xdf, 0xef, 0xc0, 0xf8, 0x23, 0x3b, 0xd8, 0x43, 0x89, 0xdc, 0xab,
-	0x3c, 0x4d, 0xad, 0xd7, 0x75, 0x20, 0xce, 0xe5, 0x2a, 0xe5, 0x72, 0x89, 0x85, 0x32, 0x95, 0x0b,
-	0x7d, 0x7c, 0xc9, 0xf4, 0xc7, 0xde, 0xa5, 0x26, 0xf5, 0x17, 0x7b, 0xe4, 0x9a, 0xd4, 0x5f, 0xfc,
-	0x29, 0x6b, 0xb6, 0xfe, 0x08, 0x97, 0xfd, 0x11, 0xe1, 0x33, 0x84, 0x29, 0xf1, 0x82, 0x13, 0x25,
-	0xde, 0xbd, 0x24, 0x9e, 0x7d, 0xd6, 0xe7, 0xb3, 0xc0, 0x9c, 0xdb, 0x35, 0xca, 0xed, 0x8a, 0x59,
-	0x4b, 0x59, 0x8b, 0x63, 0xde, 0x37, 0x6e, 0xde, 0x36, 0xd0, 0xf7, 0x01, 0x64, 0x77, 0x2b, 0xe5,
-	0x83, 0xc9, 0x8e, 0x59, 0xca, 0x07, 0x53, 0x8d, 0x31, 0x73, 0x91, 0xf2, 0xbd, 0x61, 0x5e, 0x4b,
-	0xf2, 0x0d, 0x7d, 0xdb, 0x0d, 0x5e, 0x60, 0xff, 0x16, 0x2b, 0xad, 0x07, 0x7b, 0xce, 0x90, 0x2c,
-	0xd9, 0x87, 0x62, 0xd4, 0x7c, 0x48, 0xc6, 0xdb, 0x64, 0x9b, 0x24, 0x19, 0x6f, 0x53, 0x5d, 0x8b,
-	0x78, 0xe0, 0x89, 0xed, 0x17, 0x81, 0x4a, 0x5c, 0xf0, 0x2f, 0xaa, 0x30, 0x4e, 0x8e, 0xe4, 0xe4,
-	0x78, 0x22, 0xcb, 0x3d, 0xc9, 0xd5, 0xa7, 0x2a, 0xd6, 0xc9, 0xd5, 0xa7, 0x2b, 0x45, 0xf1, 0xe3,
-	0x09, 0xb9, 0xae, 0x35, 0x58, 0x1d, 0x85, 0xac, 0xd4, 0x83, 0x92, 0x52, 0x06, 0x42, 0x1a, 0x62,
-	0xf1, 0x0a, 0x78, 0x32, 0xe1, 0x69, 0x6a, 0x48, 0xe6, 0x6b, 0x94, 0xdf, 0x79, 0x96, 0xf0, 0x28,
-	0xbf, 0x2e, 0xc3, 0x20, 0x0c, 0xf9, 0xea, 0xb8, 0xe7, 0x6b, 0x56, 0x17, 0xf7, 0xfe, 0x85, 0x6c,
-	0x84, 0xcc, 0xd5, 0x49, 0xd7, 0x7f, 0x09, 0x65, 0xb5, 0xf4, 0x83, 0x34, 0xc2, 0x27, 0x6a, 0xf4,
-	0xc9, 0x4c, 0xa2, 0xab, 0x1c, 0xc5, 0x63, 0x1b, 0x65, 0x69, 0x2b, 0x68, 0x84, 0x71, 0x1f, 0x0a,
-	0xbc, 0x04, 0xa4, 0x53, 0x69, 0xbc, 0x8c, 0xaf, 0x53, 0x69, 0xa2, 0x7e, 0x14, 0x3f, 0x3f, 0x53,
-	0x8e, 0xe4, 0x2a, 0x2a, 0xb2, 0x35, 0xe7, 0xf6, 0x10, 0x87, 0x59, 0xdc, 0x64, 0xd9, 0x36, 0x8b,
-	0x9b, 0x52, 0x21, 0xc8, 0xe2, 0xd6, 0xc3, 0x21, 0x8f, 0x07, 0xe2, 0x7a, 0x8d, 0x32, 0x88, 0xa9,
-	0x19, 0xd2, 0x3c, 0x0e, 0x45, 0x77, 0xbd, 0x91, 0x0c, 0x45, 0x7a, 0x3c, 0x04, 0x90, 0xe5, 0xa8,
-	0xe4, 0x99, 0x55, 0xdb, 0x29, 0x48, 0x9e, 0x59, 0xf5, 0x15, 0xad, 0x78, 0x8c, 0x95, 0x7c, 0xd9,
-	0xed, 0x8a, 0x70, 0xfe, 0xc2, 0x00, 0x94, 0x2e, 0x58, 0xa1, 0x77, 0xf4, 0xd4, 0xb5, 0x5d, 0x87,
-	0xfa, 0xbb, 0xaf, 0x86, 0xac, 0x0b, 0xc8, 0x52, 0xa4, 0x0e, 0xc5, 0x1e, 0xbe, 0x24, 0x42, 0x7d,
-	0x6e, 0xc0, 0x74, 0xac, 0xc8, 0x85, 0xde, 0xcc, 0xb0, 0x69, 0xa2, 0xf5, 0x50, 0x7f, 0xeb, 0x44,
-	0x3c, 0xdd, 0x61, 0x5e, 0xd9, 0x01, 0xe2, 0x56, 0xf3, 0x3b, 0x06, 0x54, 0xe2, 0xb5, 0x30, 0x94,
-	0x41, 0x3b, 0xd5, 0xb1, 0xa8, 0xdf, 0x38, 0x19, 0xf1, 0x78, 0xf3, 0xc8, 0x0b, 0x4d, 0x1f, 0x0a,
-	0xbc, 0x68, 0xa6, 0xdb, 0xf8, 0xf1, 0x16, 0x87, 0x6e, 0xe3, 0x27, 0x2a, 0x6e, 0x9a, 0x8d, 0xef,
-	0x7b, 0x7d, 0xac, 0xb8, 0x19, 0xaf, 0xa5, 0x65, 0x71, 0x3b, 0xde, 0xcd, 0x12, 0x85, 0xb8, 0x2c,
-	0x6e, 0xd2, 0xcd, 0x44, 0xc9, 0x0c, 0x65, 0x10, 0x3b, 0xc1, 0xcd, 0x92, 0x15, 0x37, 0x8d, 0x9b,
-	0x51, 0x86, 0x8a, 0x9b, 0xc9, 0x52, 0x96, 0xce, 0xcd, 0x52, 0xdd, 0x18, 0x9d, 0x9b, 0xa5, 0xab,
-	0x61, 0x1a, 0x3b, 0x52, 0xbe, 0x31, 0x37, 0x3b, 0xa7, 0x29, 0x76, 0xa1, 0x77, 0x33, 0x94, 0xa8,
-	0xed, 0xed, 0xd4, 0x6f, 0xbd, 0x22, 0x76, 0xe6, 0x1e, 0x67, 0xea, 0x17, 0x7b, 0xfc, 0x8f, 0x0c,
-	0x98, 0xd3, 0xd5, 0xc7, 0x50, 0x06, 0x9f, 0x8c, 0x56, 0x50, 0x7d, 0xf1, 0x55, 0xd1, 0x8f, 0xd7,
-	0x56, 0xb4, 0xeb, 0x1f, 0xf4, 0xbe, 0x68, 0x36, 0x9e, 0x5f, 0x85, 0x2b, 0x30, 0xd9, 0x1c, 0x3a,
-	0x8f, 0xf1, 0x11, 0x3a, 0x37, 0x95, 0xab, 0x4f, 0x13, 0xba, 0x9e, 0xef, 0x7c, 0x46, 0xff, 0x3c,
-	0xc4, 0x42, 0x6e, 0xb7, 0x0c, 0x10, 0x21, 0x8c, 0xfd, 0xdb, 0x97, 0xf3, 0xc6, 0x7f, 0x7e, 0x39,
-	0x6f, 0xfc, 0xcf, 0x97, 0xf3, 0xc6, 0x4f, 0xff, 0x6f, 0x7e, 0xec, 0xf9, 0xb5, 0x9e, 0x47, 0xc5,
-	0x5a, 0x74, 0xbc, 0x86, 0xfc, 0x93, 0x15, 0xcb, 0x0d, 0x55, 0xd4, 0xdd, 0x49, 0xfa, 0x37, 0x26,
-	0x96, 0x7f, 0x1e, 0x00, 0x00, 0xff, 0xff, 0x52, 0x87, 0xa2, 0xad, 0x3a, 0x43, 0x00, 0x00,
+	0xfe, 0x88, 0x43, 0xea, 0x11, 0xc4, 0x3e, 0x08, 0xf7, 0x86, 0xbb, 0xf4, 0x0f, 0x87, 0x2d, 0x44,
+	0xb0, 0x11, 0xf6, 0x03, 0xc7, 0x73, 0x87, 0xbb, 0xe2, 0x8b, 0x63, 0x5c, 0xe9, 0x79, 0x5e, 0xaf,
+	0x8f, 0xd9, 0x78, 0xd7, 0xf5, 0x42, 0x3b, 0x74, 0x3c, 0x37, 0xe0, 0x50, 0xf6, 0xa7, 0x73, 0xbb,
+	0x87, 0xdd, 0xdb, 0xde, 0x10, 0xbb, 0xf6, 0xd0, 0x19, 0x2d, 0x35, 0xbc, 0x21, 0xc5, 0x49, 0xe3,
+	0x9b, 0x3f, 0x31, 0xa0, 0x62, 0xe1, 0x60, 0xe8, 0xb9, 0x01, 0x7e, 0x8c, 0xed, 0x2e, 0xf6, 0xd1,
+	0x55, 0x80, 0x4e, 0xff, 0x20, 0x08, 0xb1, 0xbf, 0xe3, 0x74, 0x6b, 0xc6, 0x82, 0x71, 0x73, 0xdc,
+	0x2a, 0xf2, 0x9e, 0xb5, 0x2e, 0xba, 0x0c, 0xc5, 0x01, 0x1e, 0xec, 0x32, 0x68, 0x8e, 0x42, 0xa7,
+	0x58, 0xc7, 0x5a, 0x17, 0xd5, 0x61, 0xca, 0xc7, 0x23, 0x87, 0x88, 0x5b, 0xcb, 0x2f, 0x18, 0x37,
+	0xf3, 0x56, 0xd4, 0x26, 0x03, 0x7d, 0xfb, 0x65, 0xb8, 0x13, 0x62, 0x7f, 0x50, 0x1b, 0x67, 0x03,
+	0x49, 0x47, 0x1b, 0xfb, 0x83, 0x07, 0x85, 0x1f, 0xfc, 0x5d, 0x2d, 0xbf, 0xbc, 0x78, 0xc7, 0xfc,
+	0xe7, 0x09, 0x28, 0x5b, 0xb6, 0xdb, 0xc3, 0x16, 0xfe, 0xde, 0x01, 0x0e, 0x42, 0x54, 0x85, 0xfc,
+	0x3e, 0x3e, 0xa2, 0x72, 0x94, 0x2d, 0xf2, 0xc9, 0x08, 0xb9, 0x3d, 0xbc, 0x83, 0x5d, 0x26, 0x41,
+	0x99, 0x10, 0x72, 0x7b, 0xb8, 0xe5, 0x76, 0xd1, 0x1c, 0x4c, 0xf4, 0x9d, 0x81, 0x13, 0x72, 0xf6,
+	0xac, 0x11, 0x93, 0x6b, 0x3c, 0x21, 0xd7, 0x0a, 0x40, 0xe0, 0xf9, 0xe1, 0x8e, 0xe7, 0x77, 0xb1,
+	0x5f, 0x9b, 0x58, 0x30, 0x6e, 0x56, 0x96, 0xde, 0x5c, 0x54, 0x2d, 0xbc, 0xa8, 0x0a, 0xb4, 0xb8,
+	0xed, 0xf9, 0xe1, 0x26, 0xc1, 0xb5, 0x8a, 0x81, 0xf8, 0x44, 0x1f, 0x41, 0x89, 0x12, 0x09, 0x6d,
+	0xbf, 0x87, 0xc3, 0xda, 0x24, 0xa5, 0x72, 0xe3, 0x04, 0x2a, 0x6d, 0x8a, 0x6c, 0x51, 0xf6, 0xec,
+	0x1b, 0x99, 0x50, 0x0e, 0xb0, 0xef, 0xd8, 0x7d, 0xe7, 0x33, 0x7b, 0xb7, 0x8f, 0x6b, 0x85, 0x05,
+	0xe3, 0xe6, 0x94, 0x15, 0xeb, 0x23, 0xf3, 0xdf, 0xc7, 0x47, 0xc1, 0x8e, 0xe7, 0xf6, 0x8f, 0x6a,
+	0x53, 0x14, 0x61, 0x8a, 0x74, 0x6c, 0xba, 0xfd, 0x23, 0x6a, 0x3d, 0xef, 0xc0, 0x0d, 0x19, 0xb4,
+	0x48, 0xa1, 0x45, 0xda, 0x43, 0xc1, 0x77, 0xa1, 0x3a, 0x70, 0xdc, 0x9d, 0x81, 0xd7, 0xdd, 0x89,
+	0x14, 0x02, 0x44, 0x21, 0x0f, 0x0b, 0xbf, 0x4b, 0x2d, 0x70, 0xd7, 0xaa, 0x0c, 0x1c, 0xf7, 0xa9,
+	0xd7, 0xb5, 0x84, 0x7e, 0xc8, 0x10, 0xfb, 0x30, 0x3e, 0xa4, 0x94, 0x1c, 0x62, 0x1f, 0xaa, 0x43,
+	0xde, 0x87, 0x73, 0x84, 0x4b, 0xc7, 0xc7, 0x76, 0x88, 0xe5, 0xa8, 0x72, 0x7c, 0xd4, 0xec, 0xc0,
+	0x71, 0x57, 0x28, 0x4a, 0x6c, 0xa0, 0x7d, 0x98, 0x1a, 0x38, 0x9d, 0x1c, 0x68, 0x1f, 0xc6, 0x07,
+	0x9a, 0xef, 0x43, 0x31, 0xb2, 0x0b, 0x9a, 0x82, 0xf1, 0x8d, 0xcd, 0x8d, 0x56, 0x75, 0x0c, 0x01,
+	0x4c, 0x36, 0xb7, 0x57, 0x5a, 0x1b, 0xab, 0x55, 0x03, 0x95, 0xa0, 0xb0, 0xda, 0x62, 0x8d, 0x5c,
+	0xbd, 0xf0, 0x05, 0x5f, 0x6f, 0x4f, 0x00, 0xa4, 0x29, 0x50, 0x01, 0xf2, 0x4f, 0x5a, 0x9f, 0x56,
+	0xc7, 0x08, 0xf2, 0xf3, 0x96, 0xb5, 0xbd, 0xb6, 0xb9, 0x51, 0x35, 0x08, 0x95, 0x15, 0xab, 0xd5,
+	0x6c, 0xb7, 0xaa, 0x39, 0x82, 0xf1, 0x74, 0x73, 0xb5, 0x9a, 0x47, 0x45, 0x98, 0x78, 0xde, 0x5c,
+	0x7f, 0xd6, 0xaa, 0x8e, 0x47, 0xc4, 0xe4, 0x2a, 0xfe, 0x63, 0x03, 0xa6, 0xb9, 0xb9, 0xd9, 0xde,
+	0x42, 0xf7, 0x60, 0x72, 0x8f, 0xee, 0x2f, 0xba, 0x92, 0x4b, 0x4b, 0x57, 0x12, 0x6b, 0x23, 0xb6,
+	0x07, 0x2d, 0x8e, 0x8b, 0x4c, 0xc8, 0xef, 0x8f, 0x82, 0x5a, 0x6e, 0x21, 0x7f, 0xb3, 0xb4, 0x54,
+	0x5d, 0x64, 0x9e, 0x64, 0xf1, 0x09, 0x3e, 0x7a, 0x6e, 0xf7, 0x0f, 0xb0, 0x45, 0x80, 0x08, 0xc1,
+	0xf8, 0xc0, 0xf3, 0x31, 0x5d, 0xf0, 0x53, 0x16, 0xfd, 0x26, 0xbb, 0x80, 0xda, 0x9c, 0x2f, 0x76,
+	0xd6, 0x90, 0xe2, 0xfd, 0xbb, 0x01, 0xb0, 0x75, 0x10, 0x66, 0x6f, 0xb1, 0x39, 0x98, 0x18, 0x11,
+	0x0e, 0x7c, 0x7b, 0xb1, 0x06, 0xdd, 0x5b, 0xd8, 0x0e, 0x70, 0xb4, 0xb7, 0x48, 0x03, 0x2d, 0x40,
+	0x61, 0xe8, 0xe3, 0xd1, 0xce, 0xfe, 0x88, 0x72, 0x9b, 0x92, 0x76, 0x9a, 0x24, 0xfd, 0x4f, 0x46,
+	0xe8, 0x16, 0x94, 0x9d, 0x9e, 0xeb, 0xf9, 0x78, 0x87, 0x11, 0x9d, 0x50, 0xd1, 0x96, 0xac, 0x12,
+	0x03, 0xd2, 0x29, 0x29, 0xb8, 0x8c, 0xd5, 0xa4, 0x16, 0x77, 0x9d, 0xc0, 0xe4, 0x7c, 0x3e, 0x37,
+	0xa0, 0x44, 0xe7, 0x73, 0x2a, 0x65, 0x2f, 0xc9, 0x89, 0xe4, 0xe8, 0xb0, 0x94, 0xc2, 0x53, 0x53,
+	0x93, 0x22, 0xb8, 0x80, 0x56, 0x71, 0x1f, 0x87, 0xf8, 0x34, 0xce, 0x4b, 0x51, 0x65, 0x5e, 0xab,
+	0x4a, 0xc9, 0xef, 0xcf, 0x0c, 0x38, 0x17, 0x63, 0x78, 0xaa, 0xa9, 0xd7, 0xa0, 0xd0, 0xa5, 0xc4,
+	0x98, 0x4c, 0x79, 0x4b, 0x34, 0xd1, 0x3d, 0x98, 0xe2, 0x22, 0x05, 0xb5, 0xbc, 0x7e, 0x19, 0x4a,
+	0x29, 0x0b, 0x4c, 0xca, 0x40, 0x8a, 0xf9, 0x0f, 0x39, 0x28, 0x72, 0x65, 0x6c, 0x0e, 0x51, 0x13,
+	0xa6, 0x7d, 0xd6, 0xd8, 0xa1, 0x73, 0xe6, 0x32, 0xd6, 0xb3, 0xfd, 0xe4, 0xe3, 0x31, 0xab, 0xcc,
+	0x87, 0xd0, 0x6e, 0xf4, 0xcb, 0x50, 0x12, 0x24, 0x86, 0x07, 0x21, 0x37, 0x54, 0x2d, 0x4e, 0x40,
+	0x2e, 0xed, 0xc7, 0x63, 0x16, 0x70, 0xf4, 0xad, 0x83, 0x10, 0xb5, 0x61, 0x4e, 0x0c, 0x66, 0xf3,
+	0xe3, 0x62, 0xe4, 0x29, 0x95, 0x85, 0x38, 0x95, 0xb4, 0x39, 0x1f, 0x8f, 0x59, 0x88, 0x8f, 0x57,
+	0x80, 0x68, 0x55, 0x8a, 0x14, 0x1e, 0xb2, 0xf8, 0x92, 0x12, 0xa9, 0x7d, 0xe8, 0x72, 0x22, 0x42,
+	0x5b, 0xcb, 0x8a, 0x6c, 0xed, 0x43, 0x37, 0x52, 0xd9, 0xc3, 0x22, 0x14, 0x78, 0xb7, 0xf9, 0x6f,
+	0x39, 0x00, 0x61, 0xb1, 0xcd, 0x21, 0x5a, 0x85, 0x8a, 0xcf, 0x5b, 0x31, 0xfd, 0x5d, 0xd6, 0xea,
+	0x8f, 0x1b, 0x7a, 0xcc, 0x9a, 0x16, 0x83, 0x98, 0xb8, 0x1f, 0x42, 0x39, 0xa2, 0x22, 0x55, 0x78,
+	0x49, 0xa3, 0xc2, 0x88, 0x42, 0x49, 0x0c, 0x20, 0x4a, 0xfc, 0x04, 0xce, 0x47, 0xe3, 0x35, 0x5a,
+	0x7c, 0xe3, 0x18, 0x2d, 0x46, 0x04, 0xcf, 0x09, 0x0a, 0xaa, 0x1e, 0x1f, 0x29, 0x82, 0x49, 0x45,
+	0x5e, 0xd2, 0x28, 0x92, 0x21, 0xa9, 0x9a, 0x8c, 0x24, 0x8c, 0xa9, 0x12, 0x48, 0xd8, 0x67, 0xfd,
+	0xe6, 0x5f, 0x8c, 0x43, 0x61, 0xc5, 0x1b, 0x0c, 0x6d, 0x9f, 0x2c, 0xa2, 0x49, 0x1f, 0x07, 0x07,
+	0xfd, 0x90, 0x2a, 0xb0, 0xb2, 0x74, 0x3d, 0xce, 0x83, 0xa3, 0x89, 0xbf, 0x16, 0x45, 0xb5, 0xf8,
+	0x10, 0x32, 0x98, 0x47, 0xf9, 0xdc, 0x6b, 0x0c, 0xe6, 0x31, 0x9e, 0x0f, 0x11, 0x0e, 0x21, 0x2f,
+	0x1d, 0x42, 0x1d, 0x0a, 0xfc, 0x80, 0xc7, 0x9c, 0xf5, 0xe3, 0x31, 0x4b, 0x74, 0xa0, 0xb7, 0x61,
+	0x26, 0x19, 0x0a, 0x27, 0x38, 0x4e, 0xa5, 0x13, 0x8f, 0x9c, 0xd7, 0xa1, 0x1c, 0x8b, 0xd0, 0x93,
+	0x1c, 0xaf, 0x34, 0x50, 0xe2, 0xf2, 0x05, 0xe1, 0xd6, 0xc9, 0xb1, 0xa2, 0xfc, 0x78, 0x4c, 0x38,
+	0xf6, 0x6b, 0xc2, 0xb1, 0x4f, 0xa9, 0x81, 0x96, 0xe8, 0x95, 0xfb, 0xf8, 0x37, 0x55, 0xaf, 0xf5,
+	0x2d, 0x32, 0x38, 0x42, 0x92, 0xee, 0xcb, 0xb4, 0x60, 0x3a, 0xa6, 0x32, 0x12, 0x23, 0x5b, 0x1f,
+	0x3f, 0x6b, 0xae, 0xb3, 0x80, 0xfa, 0x88, 0xc6, 0x50, 0xab, 0x6a, 0x90, 0x00, 0xbd, 0xde, 0xda,
+	0xde, 0xae, 0xe6, 0xd0, 0x05, 0x28, 0x6e, 0x6c, 0xb6, 0x77, 0x18, 0x56, 0xbe, 0x5e, 0xf8, 0x23,
+	0xe6, 0x49, 0x64, 0x7c, 0xfe, 0x34, 0xa2, 0xc9, 0x43, 0xb4, 0x12, 0x99, 0xc7, 0x94, 0xc8, 0x6c,
+	0x88, 0xc8, 0x9c, 0x93, 0x91, 0x39, 0x8f, 0x10, 0x4c, 0xac, 0xb7, 0x9a, 0xdb, 0x34, 0x48, 0x33,
+	0xd2, 0xcb, 0xe9, 0x68, 0xfd, 0xb0, 0x02, 0x65, 0x66, 0x9e, 0x9d, 0x03, 0x97, 0x1c, 0x26, 0xfe,
+	0xd2, 0x00, 0x90, 0x1b, 0x16, 0x35, 0xa0, 0xd0, 0x61, 0x22, 0xd4, 0x0c, 0xea, 0x01, 0xcf, 0x6b,
+	0x2d, 0x6e, 0x09, 0x2c, 0x74, 0x17, 0x0a, 0xc1, 0x41, 0xa7, 0x83, 0x03, 0x11, 0xb9, 0x2f, 0x26,
+	0x9d, 0x30, 0x77, 0x88, 0x96, 0xc0, 0x23, 0x43, 0x5e, 0xda, 0x4e, 0xff, 0x80, 0xc6, 0xf1, 0xe3,
+	0x87, 0x70, 0x3c, 0xe9, 0x63, 0xff, 0xd4, 0x80, 0x92, 0xb2, 0x2d, 0x7e, 0xce, 0x10, 0x70, 0x05,
+	0x8a, 0x54, 0x18, 0xdc, 0xe5, 0x41, 0x60, 0xca, 0x92, 0x1d, 0xe8, 0x3d, 0x28, 0x8a, 0x9d, 0x24,
+	0xe2, 0x40, 0x4d, 0x4f, 0x76, 0x73, 0x68, 0x49, 0x54, 0x29, 0x64, 0x1b, 0x66, 0xa9, 0x9e, 0x3a,
+	0xe4, 0xf6, 0x21, 0x34, 0xab, 0x1e, 0xcb, 0x8d, 0xc4, 0xb1, 0xbc, 0x0e, 0x53, 0xc3, 0xbd, 0xa3,
+	0xc0, 0xe9, 0xd8, 0x7d, 0x2e, 0x4e, 0xd4, 0x96, 0x54, 0xb7, 0x01, 0xa9, 0x54, 0x4f, 0xa3, 0x00,
+	0x49, 0xf4, 0x02, 0x94, 0x1e, 0xdb, 0xc1, 0x1e, 0x17, 0x52, 0xf6, 0xdf, 0x83, 0x69, 0xd2, 0xff,
+	0xe4, 0xf9, 0x6b, 0x88, 0x2f, 0x46, 0x2d, 0x9b, 0xff, 0x68, 0x40, 0x45, 0x0c, 0x3b, 0x95, 0x81,
+	0x10, 0x8c, 0xef, 0xd9, 0xc1, 0x1e, 0x55, 0xc6, 0xb4, 0x45, 0xbf, 0xd1, 0xdb, 0x50, 0xed, 0xb0,
+	0xf9, 0xef, 0x24, 0xee, 0x5d, 0x33, 0xbc, 0x3f, 0xda, 0xfb, 0xef, 0xc2, 0x34, 0x19, 0xb2, 0x13,
+	0xbf, 0x07, 0x89, 0x6d, 0xfc, 0x9e, 0x55, 0xde, 0xa3, 0x73, 0x4e, 0x8a, 0x6f, 0x43, 0x99, 0x29,
+	0xe3, 0xac, 0x65, 0x97, 0x7a, 0xad, 0xc3, 0xcc, 0xb6, 0x6b, 0x0f, 0x83, 0x3d, 0x2f, 0x4c, 0xe8,
+	0x7c, 0xd9, 0xfc, 0x5b, 0x03, 0xaa, 0x12, 0x78, 0x2a, 0x19, 0xbe, 0x01, 0x33, 0x3e, 0x1e, 0xd8,
+	0x8e, 0xeb, 0xb8, 0xbd, 0x9d, 0xdd, 0xa3, 0x10, 0x07, 0xfc, 0xfa, 0x5a, 0x89, 0xba, 0x1f, 0x92,
+	0x5e, 0x22, 0xec, 0x6e, 0xdf, 0xdb, 0xe5, 0x4e, 0x9a, 0x7e, 0xa3, 0x37, 0xe2, 0x5e, 0xba, 0x28,
+	0xf5, 0x26, 0xfa, 0xa5, 0xcc, 0x3f, 0xcd, 0x41, 0xf9, 0x13, 0x3b, 0xec, 0x88, 0x15, 0x84, 0xd6,
+	0xa0, 0x12, 0xb9, 0x71, 0xda, 0xc3, 0xe5, 0x4e, 0x1c, 0x38, 0xe8, 0x18, 0x71, 0xaf, 0x11, 0x07,
+	0x8e, 0xe9, 0x8e, 0xda, 0x41, 0x49, 0xd9, 0x6e, 0x07, 0xf7, 0x23, 0x52, 0xb9, 0x6c, 0x52, 0x14,
+	0x51, 0x25, 0xa5, 0x76, 0xa0, 0x6f, 0x43, 0x75, 0xe8, 0x7b, 0x3d, 0x1f, 0x07, 0x41, 0x44, 0x8c,
+	0x85, 0x70, 0x53, 0x43, 0x6c, 0x8b, 0xa3, 0x26, 0x4e, 0x31, 0xf7, 0x1e, 0x8f, 0x59, 0x33, 0xc3,
+	0x38, 0x4c, 0x3a, 0xd6, 0x19, 0x79, 0xde, 0x63, 0x9e, 0xf5, 0x47, 0x79, 0x40, 0xe9, 0x69, 0x7e,
+	0xd5, 0x63, 0xf2, 0x0d, 0xa8, 0x04, 0xa1, 0xed, 0xa7, 0xd6, 0xfc, 0x34, 0xed, 0x8d, 0x56, 0xfc,
+	0x37, 0x20, 0x92, 0x6c, 0xc7, 0xf5, 0x42, 0xe7, 0xe5, 0x11, 0xbb, 0xa0, 0x58, 0x15, 0xd1, 0xbd,
+	0x41, 0x7b, 0xd1, 0x06, 0x14, 0x5e, 0x3a, 0xfd, 0x10, 0xfb, 0x41, 0x6d, 0x62, 0x21, 0x7f, 0xb3,
+	0xb2, 0xf4, 0xce, 0x49, 0x86, 0x59, 0xfc, 0x88, 0xe2, 0xb7, 0x8f, 0x86, 0xea, 0xe9, 0x97, 0x13,
+	0x51, 0x8f, 0xf1, 0x93, 0xfa, 0x1b, 0x91, 0x09, 0x53, 0xaf, 0x08, 0xd1, 0x1d, 0xa7, 0x4b, 0x63,
+	0x71, 0xb4, 0x0f, 0xef, 0x59, 0x05, 0x0a, 0x58, 0xeb, 0xa2, 0xeb, 0x30, 0xf5, 0xd2, 0xb7, 0x7b,
+	0x03, 0xec, 0x86, 0xec, 0x96, 0x2f, 0x71, 0x22, 0x80, 0xb9, 0x08, 0x20, 0x45, 0x21, 0x91, 0x6f,
+	0x63, 0x73, 0xeb, 0x59, 0xbb, 0x3a, 0x86, 0xca, 0x30, 0xb5, 0xb1, 0xb9, 0xda, 0x5a, 0x6f, 0x91,
+	0xd8, 0x28, 0x62, 0xde, 0x5d, 0xb9, 0xe9, 0x9a, 0xc2, 0x10, 0xb1, 0x35, 0xa1, 0xca, 0x65, 0xc4,
+	0x2f, 0xdd, 0x42, 0x2e, 0x41, 0xe2, 0xae, 0x79, 0x0d, 0xe6, 0x74, 0x4b, 0x43, 0x20, 0xdc, 0x33,
+	0xff, 0x25, 0x07, 0xd3, 0x7c, 0x23, 0x9c, 0x6a, 0xe7, 0x5e, 0x52, 0xa4, 0xe2, 0xd7, 0x13, 0xa1,
+	0xa4, 0x1a, 0x14, 0xd8, 0x06, 0xe9, 0xf2, 0xfb, 0xaf, 0x68, 0x12, 0xe7, 0xcc, 0xd6, 0x3b, 0xee,
+	0x72, 0xb3, 0x47, 0x6d, 0xad, 0xdb, 0x9c, 0xc8, 0x74, 0x9b, 0xd1, 0x86, 0xb3, 0x03, 0x7e, 0xb0,
+	0x2a, 0x4a, 0x53, 0x94, 0xc5, 0xa6, 0x22, 0xc0, 0x98, 0xcd, 0x0a, 0x19, 0x36, 0x43, 0x37, 0x60,
+	0x12, 0x8f, 0xb0, 0x1b, 0x06, 0xb5, 0x12, 0x0d, 0xa4, 0xd3, 0xe2, 0x42, 0xd5, 0x22, 0xbd, 0x16,
+	0x07, 0x4a, 0x53, 0x7d, 0x08, 0xb3, 0xf4, 0xbe, 0xfb, 0xc8, 0xb7, 0x5d, 0xf5, 0xce, 0xde, 0x6e,
+	0xaf, 0xf3, 0xb0, 0x43, 0x3e, 0x51, 0x05, 0x72, 0x6b, 0xab, 0x5c, 0x3f, 0xb9, 0xb5, 0x55, 0x39,
+	0xfe, 0xf7, 0x0c, 0x40, 0x2a, 0x81, 0x53, 0xd9, 0x22, 0xc1, 0x45, 0xc8, 0x91, 0x97, 0x72, 0xcc,
+	0xc1, 0x04, 0xf6, 0x7d, 0xcf, 0x67, 0x8e, 0xd2, 0x62, 0x0d, 0x29, 0xcd, 0x6d, 0x2e, 0x8c, 0x85,
+	0x47, 0xde, 0x7e, 0xe4, 0x01, 0x18, 0x59, 0x23, 0x2d, 0x7c, 0x1b, 0xce, 0xc5, 0xd0, 0xcf, 0x26,
+	0xc4, 0x6f, 0xc2, 0x0c, 0xa5, 0xba, 0xb2, 0x87, 0x3b, 0xfb, 0x43, 0xcf, 0x71, 0x53, 0x12, 0xa0,
+	0xeb, 0xc4, 0x77, 0x89, 0x70, 0x41, 0xa6, 0xc8, 0xe6, 0x5c, 0x8e, 0x3a, 0xdb, 0xed, 0x75, 0xb9,
+	0xd4, 0x77, 0xe1, 0x42, 0x82, 0xa0, 0x98, 0xd9, 0xaf, 0x40, 0xa9, 0x13, 0x75, 0x06, 0xfc, 0x04,
+	0x79, 0x35, 0x2e, 0x6e, 0x72, 0xa8, 0x3a, 0x42, 0xf2, 0xf8, 0x36, 0x5c, 0x4c, 0xf1, 0x38, 0x0b,
+	0x75, 0xdc, 0x33, 0xef, 0xc0, 0x79, 0x4a, 0xf9, 0x09, 0xc6, 0xc3, 0x66, 0xdf, 0x19, 0x9d, 0x6c,
+	0x96, 0x23, 0x3e, 0x5f, 0x65, 0xc4, 0xd7, 0xbb, 0xac, 0x24, 0xeb, 0x16, 0x67, 0xdd, 0x76, 0x06,
+	0xb8, 0xed, 0xad, 0x67, 0x4b, 0x4b, 0x02, 0xf9, 0x3e, 0x3e, 0x0a, 0xf8, 0xf1, 0x91, 0x7e, 0x4b,
+	0xef, 0xf5, 0xd7, 0x06, 0x57, 0xa7, 0x4a, 0xe7, 0x6b, 0xde, 0x1a, 0xf3, 0x00, 0x3d, 0xb2, 0x07,
+	0x71, 0x97, 0x00, 0x58, 0x6e, 0x4e, 0xe9, 0x89, 0x04, 0x26, 0x51, 0xa8, 0x9c, 0x14, 0xf8, 0x2a,
+	0xdf, 0x38, 0xf4, 0x9f, 0x20, 0x75, 0x52, 0x7a, 0x0b, 0x4a, 0x14, 0xb2, 0x1d, 0xda, 0xe1, 0x41,
+	0x90, 0x65, 0xb9, 0x65, 0xf3, 0x47, 0x06, 0xdf, 0x51, 0x82, 0xce, 0xa9, 0xe6, 0x7c, 0x17, 0x26,
+	0xe9, 0x0d, 0x51, 0xdc, 0x74, 0x2e, 0x69, 0x16, 0x36, 0x93, 0xc8, 0xe2, 0x88, 0xca, 0x39, 0xc9,
+	0x80, 0xc9, 0xa7, 0xb4, 0x72, 0xa0, 0x48, 0x3b, 0x2e, 0x2c, 0xe7, 0xda, 0x03, 0x96, 0x7e, 0x2c,
+	0x5a, 0xf4, 0x9b, 0x5e, 0x08, 0x30, 0xf6, 0x9f, 0x59, 0xeb, 0xec, 0x06, 0x52, 0xb4, 0xa2, 0x36,
+	0x51, 0x6c, 0xa7, 0xef, 0x60, 0x37, 0xa4, 0xd0, 0x71, 0x0a, 0x55, 0x7a, 0xd0, 0x0d, 0x28, 0x3a,
+	0xc1, 0x3a, 0xb6, 0x7d, 0x97, 0xa7, 0xf8, 0x15, 0xc7, 0x2c, 0x21, 0x72, 0x8d, 0x7d, 0x07, 0xaa,
+	0x4c, 0xb2, 0x66, 0xb7, 0xab, 0x9c, 0xf6, 0x23, 0xfe, 0x46, 0x82, 0x7f, 0x8c, 0x7e, 0xee, 0x64,
+	0xfa, 0x7f, 0x63, 0xc0, 0xac, 0xc2, 0xe0, 0x54, 0x26, 0x78, 0x17, 0x26, 0x59, 0xfd, 0x85, 0x1f,
+	0x05, 0xe7, 0xe2, 0xa3, 0x18, 0x1b, 0x8b, 0xe3, 0xa0, 0x45, 0x28, 0xb0, 0x2f, 0x71, 0x8d, 0xd3,
+	0xa3, 0x0b, 0x24, 0x29, 0xf2, 0x22, 0x9c, 0xe3, 0x30, 0x3c, 0xf0, 0x74, 0x7b, 0x6e, 0x3c, 0xee,
+	0x21, 0x7e, 0x68, 0xc0, 0x5c, 0x7c, 0xc0, 0xa9, 0x66, 0xa9, 0xc8, 0x9d, 0xfb, 0x4a, 0x72, 0xff,
+	0xaa, 0x90, 0xfb, 0xd9, 0xb0, 0xab, 0x1c, 0x39, 0x93, 0x2b, 0x4e, 0xb5, 0x6e, 0x2e, 0x6e, 0x5d,
+	0x49, 0xeb, 0x27, 0xd1, 0x9c, 0x04, 0xb1, 0x53, 0xcd, 0xe9, 0xfd, 0xd7, 0x9a, 0x93, 0x72, 0x04,
+	0x4b, 0x4d, 0x6e, 0x4d, 0x2c, 0xa3, 0x75, 0x27, 0x88, 0x22, 0xce, 0x3b, 0x50, 0xee, 0x3b, 0x2e,
+	0xb6, 0x7d, 0x5e, 0x43, 0x32, 0xd4, 0xf5, 0x78, 0xdf, 0x8a, 0x01, 0x25, 0xa9, 0xdf, 0x32, 0x00,
+	0xa9, 0xb4, 0x7e, 0x31, 0xd6, 0x6a, 0x08, 0x05, 0x6f, 0xf9, 0xde, 0xc0, 0x0b, 0x4f, 0x5a, 0x66,
+	0xf7, 0xcc, 0xdf, 0x31, 0xe0, 0x7c, 0x62, 0xc4, 0x2f, 0x42, 0xf2, 0x7b, 0xe6, 0x15, 0x98, 0x5d,
+	0xc5, 0xe2, 0x8c, 0x97, 0xca, 0x1d, 0x6c, 0x03, 0x52, 0xa1, 0x67, 0x73, 0x8a, 0xf9, 0x26, 0xcc,
+	0x3e, 0xf5, 0x46, 0xc4, 0x91, 0x13, 0xb0, 0x74, 0x53, 0x2c, 0x99, 0x15, 0xe9, 0x2b, 0x6a, 0x4b,
+	0xd7, 0xbb, 0x0d, 0x48, 0x1d, 0x79, 0x16, 0xe2, 0x2c, 0x9b, 0xff, 0x63, 0x40, 0xb9, 0xd9, 0xb7,
+	0xfd, 0x81, 0x10, 0xe5, 0x43, 0x98, 0x64, 0x99, 0x19, 0x9e, 0x66, 0x7d, 0x2b, 0x4e, 0x4f, 0xc5,
+	0x65, 0x8d, 0x26, 0xcb, 0xe3, 0xf0, 0x51, 0x64, 0x2a, 0xbc, 0xb2, 0xbc, 0x9a, 0xa8, 0x34, 0xaf,
+	0xa2, 0xdb, 0x30, 0x61, 0x93, 0x21, 0x34, 0xbc, 0x56, 0x92, 0xe9, 0x32, 0x4a, 0x8d, 0x5c, 0x89,
+	0x2c, 0x86, 0x65, 0x7e, 0x00, 0x25, 0x85, 0x03, 0x2a, 0x40, 0xfe, 0x51, 0x8b, 0x5f, 0x93, 0x9a,
+	0x2b, 0xed, 0xb5, 0xe7, 0x2c, 0x85, 0x58, 0x01, 0x58, 0x6d, 0x45, 0xed, 0x9c, 0xa6, 0xb0, 0x67,
+	0x73, 0x3a, 0x3c, 0x6e, 0xa9, 0x12, 0x1a, 0x59, 0x12, 0xe6, 0x5e, 0x47, 0x42, 0xc9, 0xe2, 0x37,
+	0x0d, 0x98, 0xe6, 0xaa, 0x39, 0x6d, 0x68, 0xa6, 0x94, 0x33, 0x42, 0xb3, 0x32, 0x0d, 0x8b, 0x23,
+	0x4a, 0x19, 0xfe, 0xc9, 0x80, 0xea, 0xaa, 0xf7, 0xca, 0xed, 0xf9, 0x76, 0x37, 0xda, 0x83, 0x1f,
+	0x25, 0xcc, 0xb9, 0x98, 0xc8, 0xf4, 0x27, 0xf0, 0x65, 0x47, 0xc2, 0xac, 0x35, 0x99, 0x4b, 0x61,
+	0xf1, 0x5d, 0x34, 0xcd, 0x6f, 0xc1, 0x4c, 0x62, 0x10, 0x31, 0xd0, 0xf3, 0xe6, 0xfa, 0xda, 0x2a,
+	0x31, 0x08, 0xcd, 0xf7, 0xb6, 0x36, 0x9a, 0x0f, 0xd7, 0x5b, 0xbc, 0x2a, 0xdb, 0xdc, 0x58, 0x69,
+	0xad, 0x4b, 0x43, 0xdd, 0x17, 0x33, 0xb8, 0x6f, 0xf6, 0x61, 0x56, 0x11, 0xe8, 0xb4, 0xc5, 0x31,
+	0xbd, 0xbc, 0x92, 0xdb, 0x37, 0xe1, 0x72, 0xc4, 0xed, 0x39, 0x03, 0xb6, 0x71, 0xa0, 0x5e, 0xd6,
+	0x46, 0x9c, 0x69, 0xd1, 0x22, 0x9f, 0x62, 0xe4, 0x7b, 0x66, 0x0d, 0xa6, 0xf9, 0xf9, 0x28, 0xe9,
+	0x32, 0xfe, 0x2b, 0x0f, 0x15, 0x01, 0xfa, 0x7a, 0xe4, 0x47, 0x17, 0x60, 0xb2, 0xbb, 0xbb, 0xed,
+	0x7c, 0x26, 0x2a, 0xba, 0xbc, 0x45, 0xfa, 0xfb, 0x8c, 0x0f, 0x7b, 0xa7, 0xc1, 0x5b, 0xe8, 0x0a,
+	0x7b, 0xc2, 0xb1, 0xe6, 0x76, 0xf1, 0x21, 0x3d, 0x46, 0x8d, 0x5b, 0xb2, 0x83, 0xa6, 0x43, 0xf9,
+	0x7b, 0x0e, 0x7a, 0x4b, 0x56, 0xde, 0x77, 0xa0, 0x65, 0xa8, 0x92, 0xef, 0xe6, 0x70, 0xd8, 0x77,
+	0x70, 0x97, 0x11, 0x20, 0x17, 0xe4, 0x71, 0x79, 0x4e, 0x4a, 0x21, 0xa0, 0x6b, 0x30, 0x49, 0x2f,
+	0x8f, 0x41, 0x6d, 0x8a, 0x44, 0x64, 0x89, 0xca, 0xbb, 0xd1, 0xdb, 0x50, 0x62, 0x12, 0xaf, 0xb9,
+	0xcf, 0x02, 0x4c, 0x5f, 0x3b, 0x28, 0x99, 0x14, 0x15, 0x16, 0x3f, 0xa1, 0x41, 0xd6, 0x09, 0x0d,
+	0x35, 0xa0, 0x12, 0x84, 0x9e, 0x6f, 0xf7, 0x84, 0x19, 0xe9, 0x53, 0x07, 0x25, 0xdd, 0x97, 0x00,
+	0x4b, 0x11, 0x3e, 0x3e, 0xf0, 0x42, 0x3b, 0xfe, 0xc4, 0xe1, 0x3d, 0x4b, 0x85, 0x49, 0xcb, 0x5e,
+	0x81, 0xd9, 0xe6, 0x41, 0xb8, 0xd7, 0x72, 0x49, 0x04, 0x4e, 0xd9, 0xfd, 0x2a, 0x20, 0x02, 0x5d,
+	0x75, 0x02, 0x2d, 0x98, 0x0f, 0xd6, 0x2e, 0x9a, 0xfb, 0xe6, 0x06, 0x9c, 0x23, 0x50, 0xec, 0x86,
+	0x4e, 0x47, 0x39, 0xed, 0x88, 0xf3, 0xb4, 0x91, 0x38, 0x4f, 0xdb, 0x41, 0xf0, 0xca, 0xf3, 0xbb,
+	0x7c, 0x5d, 0x44, 0x6d, 0xc9, 0xed, 0xef, 0x0d, 0x26, 0xcd, 0xb3, 0x20, 0x76, 0x16, 0xfe, 0x8a,
+	0xf4, 0xd0, 0x2f, 0x41, 0x81, 0xbf, 0x41, 0xe2, 0x29, 0xc6, 0x0b, 0x8b, 0xec, 0xed, 0xd3, 0x22,
+	0x27, 0xbc, 0xc9, 0xa0, 0x4a, 0x1a, 0x8c, 0xe3, 0x13, 0x8b, 0xec, 0xd9, 0xc1, 0x1e, 0xee, 0x6e,
+	0x09, 0xe2, 0xb1, 0x04, 0xec, 0x7d, 0x2b, 0x01, 0x96, 0xb2, 0xdf, 0x95, 0xa2, 0x3f, 0xc2, 0xe1,
+	0x31, 0xa2, 0xab, 0x29, 0xfe, 0xf3, 0x62, 0x08, 0xaf, 0x4c, 0xbe, 0xce, 0xa8, 0x1f, 0x1b, 0x70,
+	0x55, 0x0c, 0x5b, 0xd9, 0xb3, 0xdd, 0x1e, 0x16, 0xc2, 0xfc, 0xbc, 0xfa, 0x4a, 0x4f, 0x3a, 0xff,
+	0x9a, 0x93, 0x7e, 0x02, 0xb5, 0x68, 0xd2, 0x34, 0xdd, 0xe3, 0xf5, 0xd5, 0x49, 0x1c, 0x04, 0x91,
+	0x1f, 0xa2, 0xdf, 0xa4, 0xcf, 0xf7, 0xfa, 0xd1, 0x4d, 0x8b, 0x7c, 0x4b, 0x62, 0xeb, 0x70, 0x49,
+	0x10, 0xe3, 0xf9, 0x97, 0x38, 0xb5, 0xd4, 0x9c, 0x8e, 0xa5, 0xc6, 0xed, 0x41, 0x68, 0x1c, 0xbf,
+	0x94, 0xb4, 0x43, 0xe2, 0x26, 0xa4, 0x5c, 0x0c, 0x1d, 0x97, 0x79, 0xb6, 0x03, 0x88, 0xcc, 0xca,
+	0xa1, 0x38, 0x05, 0x27, 0x24, 0xb5, 0x70, 0xbe, 0x04, 0x08, 0x3c, 0xb5, 0x04, 0xb2, 0xb9, 0x62,
+	0x98, 0x8f, 0x04, 0x25, 0x6a, 0xdf, 0xc2, 0xfe, 0xc0, 0x09, 0x02, 0xa5, 0xd6, 0xa5, 0x53, 0xd7,
+	0x5b, 0x30, 0x3e, 0xc4, 0xfc, 0x84, 0x50, 0x5a, 0x42, 0x62, 0x4f, 0x28, 0x83, 0x29, 0x5c, 0xb2,
+	0x19, 0xc0, 0x35, 0xc1, 0x86, 0x19, 0x44, 0xcb, 0x27, 0x29, 0xa6, 0xc8, 0xaf, 0xe7, 0x32, 0xf2,
+	0xeb, 0xf9, 0x78, 0x7e, 0x3d, 0x76, 0x6a, 0x55, 0x1d, 0xd5, 0xd9, 0x9c, 0x5a, 0xdb, 0xcc, 0x00,
+	0x91, 0x7f, 0x3b, 0x1b, 0xaa, 0xbf, 0xcf, 0x1d, 0xd5, 0x59, 0x45, 0x4c, 0x4c, 0xe7, 0x2c, 0x2a,
+	0xa1, 0xa2, 0x89, 0x4c, 0x28, 0x13, 0x23, 0x59, 0x6a, 0xe1, 0x61, 0xdc, 0x8a, 0xf5, 0x49, 0x67,
+	0xbc, 0x0f, 0x73, 0x71, 0x67, 0x7c, 0x2a, 0xa1, 0xe6, 0x60, 0x22, 0xf4, 0xf6, 0xb1, 0x08, 0xe2,
+	0xac, 0x91, 0x52, 0x6b, 0xe4, 0xa8, 0xcf, 0x46, 0xad, 0xdf, 0x95, 0x54, 0xe9, 0x06, 0x3c, 0xed,
+	0x0c, 0xc8, 0x72, 0x14, 0x17, 0x6c, 0xd6, 0x90, 0xbc, 0x3e, 0x81, 0x0b, 0x49, 0xe7, 0x7b, 0x36,
+	0x93, 0xd8, 0x61, 0x9b, 0x53, 0xe7, 0x9e, 0xcf, 0x86, 0xc1, 0x0b, 0xe9, 0x27, 0x15, 0xa7, 0x7b,
+	0x36, 0xb4, 0x7f, 0x0d, 0xea, 0x3a, 0x1f, 0x7c, 0xa6, 0x7b, 0x31, 0x72, 0xc9, 0x67, 0x43, 0xf5,
+	0x87, 0x86, 0x24, 0xab, 0xae, 0x9a, 0x0f, 0xbe, 0x0a, 0x59, 0x11, 0xeb, 0xee, 0x44, 0xcb, 0xa7,
+	0x11, 0x79, 0xcb, 0xbc, 0xde, 0x5b, 0xca, 0x21, 0x14, 0x51, 0xec, 0x3f, 0xe9, 0xea, 0xbf, 0xce,
+	0xd5, 0xcb, 0x99, 0xc9, 0xb8, 0x73, 0x5a, 0x66, 0x24, 0x3c, 0x47, 0xcc, 0x68, 0x23, 0xb5, 0x55,
+	0xd4, 0x20, 0x75, 0x36, 0xa6, 0xfb, 0x75, 0x19, 0x60, 0x52, 0x71, 0xec, 0x6c, 0x38, 0xd8, 0xb0,
+	0x90, 0x1d, 0xc2, 0xce, 0x84, 0xc5, 0xad, 0x26, 0x14, 0xa3, 0xeb, 0xb5, 0xf2, 0x18, 0xb8, 0x04,
+	0x85, 0x8d, 0xcd, 0xed, 0xad, 0xe6, 0x0a, 0xb9, 0x3d, 0xce, 0x41, 0x61, 0x65, 0xd3, 0xb2, 0x9e,
+	0x6d, 0xb5, 0xc9, 0xf5, 0x31, 0xf9, 0x36, 0x68, 0xe9, 0x67, 0x79, 0xc8, 0x3d, 0x79, 0x8e, 0x3e,
+	0x85, 0x09, 0xf6, 0x36, 0xed, 0x98, 0x27, 0x8a, 0xf5, 0xe3, 0x9e, 0xdf, 0x99, 0x17, 0x7f, 0xf0,
+	0x9f, 0x3f, 0xfb, 0x83, 0xdc, 0xac, 0x59, 0x6e, 0x8c, 0x96, 0x1b, 0xfb, 0xa3, 0x06, 0x0d, 0xb2,
+	0x0f, 0x8c, 0x5b, 0xe8, 0x63, 0xc8, 0x6f, 0x1d, 0x84, 0x28, 0xf3, 0xe9, 0x62, 0x3d, 0xfb, 0x45,
+	0x9e, 0x79, 0x9e, 0x12, 0x9d, 0x31, 0x81, 0x13, 0x1d, 0x1e, 0x84, 0x84, 0xe4, 0xf7, 0xa0, 0xa4,
+	0xbe, 0xa7, 0x3b, 0xf1, 0x3d, 0x63, 0xfd, 0xe4, 0xb7, 0x7a, 0xe6, 0x55, 0xca, 0xea, 0xa2, 0x89,
+	0x38, 0x2b, 0xf6, 0xe2, 0x4f, 0x9d, 0x45, 0xfb, 0xd0, 0x45, 0x99, 0xaf, 0x1d, 0xeb, 0xd9, 0xcf,
+	0xf7, 0x52, 0xb3, 0x08, 0x0f, 0x5d, 0x42, 0xf2, 0xbb, 0xfc, 0x9d, 0x5e, 0x27, 0x44, 0xd7, 0x34,
+	0x0f, 0xad, 0xd4, 0x07, 0x44, 0xf5, 0x85, 0x6c, 0x04, 0xce, 0xe4, 0x0a, 0x65, 0x72, 0xc1, 0x9c,
+	0xe5, 0x4c, 0x3a, 0x11, 0xca, 0x03, 0xe3, 0xd6, 0x52, 0x07, 0x26, 0x68, 0x81, 0x1a, 0xbd, 0x10,
+	0x1f, 0x75, 0x4d, 0xe9, 0x3f, 0xc3, 0xd0, 0xb1, 0xd2, 0xb6, 0x39, 0x47, 0x19, 0x55, 0xcc, 0x22,
+	0x61, 0x44, 0xcb, 0xd3, 0x0f, 0x8c, 0x5b, 0x37, 0x8d, 0x3b, 0xc6, 0xd2, 0x5f, 0x4d, 0xc0, 0x04,
+	0x2d, 0x84, 0xa0, 0x7d, 0x00, 0x59, 0x88, 0x4d, 0xce, 0x2e, 0x55, 0xe3, 0x4d, 0xce, 0x2e, 0x5d,
+	0xc3, 0x35, 0xeb, 0x94, 0xe9, 0x9c, 0x39, 0x43, 0x98, 0xd2, 0xfa, 0x4a, 0x83, 0x96, 0x93, 0x88,
+	0x1e, 0x7f, 0x6c, 0xf0, 0x8a, 0x10, 0xdb, 0x66, 0x48, 0x47, 0x2d, 0x56, 0x84, 0x4d, 0x2e, 0x07,
+	0x4d, 0xdd, 0xd5, 0xbc, 0x4f, 0x19, 0x36, 0xcc, 0xaa, 0x64, 0xe8, 0x53, 0x8c, 0x07, 0xc6, 0xad,
+	0x17, 0x35, 0xf3, 0x1c, 0xd7, 0x72, 0x02, 0x82, 0xbe, 0x0f, 0x95, 0x78, 0xb9, 0x10, 0x5d, 0xd7,
+	0xf0, 0x4a, 0x96, 0x1f, 0xeb, 0x6f, 0x1e, 0x8f, 0xc4, 0x65, 0x9a, 0xa7, 0x32, 0x71, 0xe6, 0x8c,
+	0xf3, 0x3e, 0xc6, 0x43, 0x9b, 0x20, 0x71, 0x1b, 0xa0, 0x3f, 0x31, 0x78, 0xc5, 0x57, 0x56, 0xfb,
+	0x90, 0x8e, 0x7a, 0xaa, 0xa8, 0x58, 0xbf, 0x71, 0x02, 0x16, 0x17, 0xe2, 0x03, 0x2a, 0xc4, 0xfb,
+	0xe6, 0x9c, 0x14, 0x22, 0x74, 0x06, 0x38, 0xf4, 0xb8, 0x14, 0x2f, 0xae, 0x98, 0x17, 0x63, 0xca,
+	0x89, 0x41, 0xa5, 0xb1, 0x58, 0x55, 0x4e, 0x6b, 0xac, 0x58, 0xe1, 0x4f, 0x6b, 0xac, 0x78, 0x49,
+	0x4f, 0x67, 0x2c, 0x5e, 0x83, 0xd3, 0x18, 0x2b, 0x82, 0x2c, 0xfd, 0xdf, 0x38, 0x14, 0x56, 0xd8,
+	0xff, 0xf7, 0x41, 0x1e, 0x14, 0xa3, 0x3a, 0x15, 0x9a, 0xd7, 0xa5, 0xc2, 0xe5, 0x55, 0xae, 0x7e,
+	0x2d, 0x13, 0xce, 0x05, 0x7a, 0x83, 0x0a, 0x74, 0xd9, 0xbc, 0x40, 0x38, 0xf3, 0xff, 0x52, 0xd4,
+	0x60, 0x09, 0xd3, 0x86, 0xdd, 0xed, 0x12, 0x45, 0xfc, 0x06, 0x94, 0xd5, 0xaa, 0x11, 0x7a, 0x43,
+	0x9b, 0x7e, 0x57, 0x4b, 0x50, 0x75, 0xf3, 0x38, 0x14, 0xce, 0xf9, 0x4d, 0xca, 0x79, 0xde, 0xbc,
+	0xa4, 0xe1, 0xec, 0x53, 0xd4, 0x18, 0x73, 0x56, 0xde, 0xd1, 0x33, 0x8f, 0xd5, 0x91, 0xf4, 0xcc,
+	0xe3, 0xd5, 0xa1, 0x63, 0x99, 0x1f, 0x50, 0x54, 0xc2, 0x3c, 0x00, 0x90, 0xf5, 0x17, 0xa4, 0xd5,
+	0xa5, 0x72, 0x61, 0x4d, 0x3a, 0x87, 0x74, 0xe9, 0xc6, 0x34, 0x29, 0x5b, 0xbe, 0xee, 0x12, 0x6c,
+	0xfb, 0x4e, 0x10, 0xb2, 0x8d, 0x39, 0x1d, 0xab, 0x9e, 0x20, 0xed, 0x7c, 0xe2, 0xc5, 0x98, 0xfa,
+	0xf5, 0x63, 0x71, 0x38, 0xf7, 0x1b, 0x94, 0xfb, 0x35, 0xb3, 0xae, 0xe1, 0x3e, 0x64, 0xb8, 0x64,
+	0xb1, 0x7d, 0x5e, 0x80, 0xd2, 0x53, 0xdb, 0x71, 0x43, 0xec, 0xda, 0x6e, 0x07, 0xa3, 0x5d, 0x98,
+	0xa0, 0xb1, 0x3b, 0xe9, 0x88, 0xd5, 0x62, 0x41, 0xd2, 0x11, 0xc7, 0xb2, 0xe5, 0xe6, 0x02, 0x65,
+	0x5c, 0x37, 0xcf, 0x13, 0xc6, 0x03, 0x49, 0xba, 0xc1, 0xf2, 0xec, 0xc6, 0x2d, 0xf4, 0x12, 0x26,
+	0x79, 0x95, 0x3c, 0x41, 0x28, 0x96, 0x54, 0xab, 0x5f, 0xd1, 0x03, 0x75, 0x6b, 0x59, 0x65, 0x13,
+	0x50, 0x3c, 0xc2, 0x67, 0x04, 0x20, 0x8b, 0x3e, 0x49, 0x8b, 0xa6, 0x8a, 0x45, 0xf5, 0x85, 0x6c,
+	0x04, 0x9d, 0x4e, 0x55, 0x9e, 0xdd, 0x08, 0x97, 0xf0, 0xfd, 0x0e, 0x8c, 0x3f, 0xb6, 0x83, 0x3d,
+	0x94, 0x88, 0xbd, 0xca, 0xa3, 0xd6, 0x7a, 0x5d, 0x07, 0xe2, 0x5c, 0xae, 0x51, 0x2e, 0x97, 0x98,
+	0x2b, 0x53, 0xb9, 0xd0, 0x67, 0x9b, 0x4c, 0x7f, 0xec, 0x45, 0x6b, 0x52, 0x7f, 0xb1, 0xe7, 0xb1,
+	0x49, 0xfd, 0xc5, 0x1f, 0xc1, 0x66, 0xeb, 0x8f, 0x70, 0xd9, 0x1f, 0x11, 0x3e, 0x43, 0x98, 0x12,
+	0x6f, 0x3f, 0x51, 0xe2, 0xc5, 0x4c, 0xe2, 0xc1, 0x68, 0x7d, 0x3e, 0x0b, 0xcc, 0xb9, 0x5d, 0xa7,
+	0xdc, 0xae, 0x9a, 0xb5, 0x94, 0xb5, 0x38, 0xe6, 0x03, 0xe3, 0xd6, 0x1d, 0x03, 0x7d, 0x1f, 0x40,
+	0xd6, 0xc5, 0x52, 0x7b, 0x30, 0x59, 0x6b, 0x4b, 0xed, 0xc1, 0x54, 0x49, 0xcd, 0x5c, 0xa4, 0x7c,
+	0x6f, 0x9a, 0xd7, 0x93, 0x7c, 0x43, 0xdf, 0x76, 0x83, 0x97, 0xd8, 0xbf, 0xcd, 0x52, 0xeb, 0xc1,
+	0x9e, 0x33, 0x24, 0x53, 0xf6, 0xa1, 0x18, 0x15, 0x12, 0x92, 0xfe, 0x36, 0x59, 0x60, 0x49, 0xfa,
+	0xdb, 0x54, 0xbd, 0x23, 0xee, 0x78, 0x62, 0xeb, 0x45, 0xa0, 0x92, 0x2d, 0xf8, 0xe7, 0x55, 0x18,
+	0x27, 0x47, 0x72, 0x72, 0x3c, 0x91, 0xe9, 0x9e, 0xe4, 0xec, 0x53, 0x19, 0xeb, 0xe4, 0xec, 0xd3,
+	0x99, 0xa2, 0xf8, 0xf1, 0x84, 0x5c, 0xd7, 0x1a, 0x2c, 0x8f, 0x42, 0x66, 0xea, 0x41, 0x49, 0x49,
+	0x03, 0x21, 0x0d, 0xb1, 0x78, 0x06, 0x3c, 0x19, 0xf0, 0x34, 0x39, 0x24, 0xf3, 0x32, 0xe5, 0x77,
+	0x9e, 0x05, 0x3c, 0xca, 0xaf, 0xcb, 0x30, 0x08, 0x43, 0x3e, 0x3b, 0xbe, 0xf3, 0x35, 0xb3, 0x8b,
+	0xef, 0xfe, 0x85, 0x6c, 0x84, 0xcc, 0xd9, 0xc9, 0xad, 0xff, 0x0a, 0xca, 0x6a, 0xea, 0x07, 0x69,
+	0x84, 0x4f, 0xe4, 0xe8, 0x93, 0x91, 0x44, 0x97, 0x39, 0x8a, 0xfb, 0x36, 0xca, 0xd2, 0x56, 0xd0,
+	0x08, 0xe3, 0x3e, 0x14, 0x78, 0x0a, 0x48, 0xa7, 0xd2, 0x78, 0x1a, 0x5f, 0xa7, 0xd2, 0x44, 0xfe,
+	0x28, 0x7e, 0x7e, 0xa6, 0x1c, 0xc9, 0x55, 0x54, 0x44, 0x6b, 0xce, 0xed, 0x11, 0x0e, 0xb3, 0xb8,
+	0xc9, 0xb4, 0x6d, 0x16, 0x37, 0x25, 0x43, 0x90, 0xc5, 0xad, 0x87, 0x43, 0xee, 0x0f, 0xc4, 0xf5,
+	0x1a, 0x65, 0x10, 0x53, 0x23, 0xa4, 0x79, 0x1c, 0x8a, 0xee, 0x7a, 0x23, 0x19, 0x8a, 0xf0, 0x78,
+	0x08, 0x20, 0xd3, 0x51, 0xc9, 0x33, 0xab, 0xb6, 0x52, 0x90, 0x3c, 0xb3, 0xea, 0x33, 0x5a, 0x71,
+	0x1f, 0x2b, 0xf9, 0xb2, 0xdb, 0x15, 0xe1, 0xfc, 0x85, 0x01, 0x28, 0x9d, 0xb0, 0x42, 0xef, 0xe8,
+	0xa9, 0x6b, 0xab, 0x0e, 0xf5, 0x77, 0x5f, 0x0f, 0x59, 0xe7, 0x90, 0xa5, 0x48, 0x1d, 0x8a, 0x3d,
+	0x7c, 0x45, 0x84, 0xfa, 0xdc, 0x80, 0xe9, 0x58, 0x92, 0x0b, 0xbd, 0x95, 0x61, 0xd3, 0x44, 0xe9,
+	0xa1, 0xfe, 0x8d, 0x13, 0xf1, 0x74, 0x87, 0x79, 0x65, 0x05, 0x88, 0x5b, 0xcd, 0x6f, 0x1b, 0x50,
+	0x89, 0xe7, 0xc2, 0x50, 0x06, 0xed, 0x54, 0xc5, 0xa2, 0x7e, 0xf3, 0x64, 0xc4, 0xe3, 0xcd, 0x23,
+	0x2f, 0x34, 0x7d, 0x28, 0xf0, 0xa4, 0x99, 0x6e, 0xe1, 0xc7, 0x4b, 0x1c, 0xba, 0x85, 0x9f, 0xc8,
+	0xb8, 0x69, 0x16, 0xbe, 0xef, 0xf5, 0xb1, 0xb2, 0xcd, 0x78, 0x2e, 0x2d, 0x8b, 0xdb, 0xf1, 0xdb,
+	0x2c, 0x91, 0x88, 0xcb, 0xe2, 0x26, 0xb7, 0x99, 0x48, 0x99, 0xa1, 0x0c, 0x62, 0x27, 0x6c, 0xb3,
+	0x64, 0xc6, 0x4d, 0xb3, 0xcd, 0x28, 0x43, 0x65, 0x9b, 0xc9, 0x54, 0x96, 0x6e, 0x9b, 0xa5, 0xaa,
+	0x31, 0xba, 0x6d, 0x96, 0xce, 0x86, 0x69, 0xec, 0x48, 0xf9, 0xc6, 0xb6, 0xd9, 0x39, 0x4d, 0xb2,
+	0x0b, 0xbd, 0x9b, 0xa1, 0x44, 0x6d, 0x6d, 0xa7, 0x7e, 0xfb, 0x35, 0xb1, 0x33, 0xd7, 0x38, 0x53,
+	0xbf, 0x58, 0xe3, 0x7f, 0x68, 0xc0, 0x9c, 0x2e, 0x3f, 0x86, 0x32, 0xf8, 0x64, 0x94, 0x82, 0xea,
+	0x8b, 0xaf, 0x8b, 0x7e, 0xbc, 0xb6, 0xa2, 0x55, 0xff, 0xb0, 0xf7, 0x45, 0xb3, 0xf1, 0xe2, 0x1a,
+	0x5c, 0x85, 0xc9, 0xe6, 0xd0, 0x79, 0x82, 0x8f, 0xd0, 0xb9, 0xa9, 0x5c, 0x7d, 0x9a, 0xd0, 0xf5,
+	0x7c, 0xe7, 0x33, 0xfa, 0xc3, 0x12, 0x0b, 0xb9, 0xdd, 0x32, 0x40, 0x84, 0x30, 0xf6, 0xaf, 0x5f,
+	0xce, 0x1b, 0xff, 0xf1, 0xe5, 0xbc, 0xf1, 0xdf, 0x5f, 0xce, 0x1b, 0x3f, 0xfd, 0xdf, 0xf9, 0xb1,
+	0x17, 0xd7, 0x7b, 0x1e, 0x15, 0x6b, 0xd1, 0xf1, 0x1a, 0xf2, 0xc7, 0x2e, 0x96, 0x1b, 0xaa, 0xa8,
+	0xbb, 0x93, 0xf4, 0xd7, 0x29, 0x96, 0xff, 0x3f, 0x00, 0x00, 0xff, 0xff, 0x97, 0xb0, 0x67, 0xa9,
+	0x74, 0x43, 0x00, 0x00,
 }
 
 // Reference imports to suppress errors if they are not otherwise used.
@@ -11509,6 +11563,40 @@ func (m *DowngradeResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 	return len(dAtA) - i, nil
 }
 
+func (m *DowngradeVersionTestRequest) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *DowngradeVersionTestRequest) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *DowngradeVersionTestRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if m.XXX_unrecognized != nil {
+		i -= len(m.XXX_unrecognized)
+		copy(dAtA[i:], m.XXX_unrecognized)
+	}
+	if len(m.Ver) > 0 {
+		i -= len(m.Ver)
+		copy(dAtA[i:], m.Ver)
+		i = encodeVarintRpc(dAtA, i, uint64(len(m.Ver)))
+		i--
+		dAtA[i] = 0xa
+	}
+	return len(dAtA) - i, nil
+}
+
 func (m *StatusRequest) Marshal() (dAtA []byte, err error) {
 	size := m.Size()
 	dAtA = make([]byte, size)
@@ -14434,6 +14522,22 @@ func (m *DowngradeResponse) Size() (n int) {
 	return n
 }
 
+func (m *DowngradeVersionTestRequest) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	l = len(m.Ver)
+	if l > 0 {
+		n += 1 + l + sovRpc(uint64(l))
+	}
+	if m.XXX_unrecognized != nil {
+		n += len(m.XXX_unrecognized)
+	}
+	return n
+}
+
 func (m *StatusRequest) Size() (n int) {
 	if m == nil {
 		return 0
@@ -22301,6 +22405,89 @@ func (m *DowngradeResponse) Unmarshal(dAtA []byte) error {
 	}
 	return nil
 }
+func (m *DowngradeVersionTestRequest) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowRpc
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: DowngradeVersionTestRequest: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: DowngradeVersionTestRequest: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Ver", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowRpc
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthRpc
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthRpc
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Ver = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipRpc(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthRpc
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
 func (m *StatusRequest) Unmarshal(dAtA []byte) error {
 	l := len(dAtA)
 	iNdEx := 0
diff --git a/api/etcdserverpb/rpc.proto b/api/etcdserverpb/rpc.proto
index a4ace71ce..604be785f 100644
--- a/api/etcdserverpb/rpc.proto
+++ b/api/etcdserverpb/rpc.proto
@@ -1158,6 +1158,16 @@ message DowngradeResponse {
   string version = 2;
 }
 
+// DowngradeVersionTestRequest is used for test only. The version in
+// this request will be read as the WAL record version.If the downgrade
+// target version is less than this version, then the downgrade(online)
+// or migration(offline) isn't safe, so shouldn't be allowed.
+message DowngradeVersionTestRequest {
+  option (versionpb.etcd_version_msg) = "3.6";
+
+  string ver = 1;
+}
+
 message StatusRequest {
   option (versionpb.etcd_version_msg) = "3.0";
 }
diff --git a/api/go.mod b/api/go.mod
index d32750222..d56154d3c 100644
--- a/api/go.mod
+++ b/api/go.mod
@@ -8,23 +8,23 @@ require (
 	github.com/coreos/go-semver v0.3.1
 	github.com/gogo/protobuf v1.3.2
 	github.com/golang/protobuf v1.5.4
-	github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1
+	github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.0
 	github.com/stretchr/testify v1.10.0
-	google.golang.org/genproto/googleapis/api v0.0.0-20250102185135-69823020774d
-	google.golang.org/grpc v1.69.4
-	google.golang.org/protobuf v1.36.2
+	google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f
+	google.golang.org/grpc v1.70.0
+	google.golang.org/protobuf v1.36.4
 )
 
 require (
 	github.com/davecgh/go-spew v1.1.1 // indirect
 	github.com/pmezard/go-difflib v1.0.0 // indirect
 	github.com/rogpeppe/go-internal v1.13.1 // indirect
-	go.opentelemetry.io/otel v1.33.0 // indirect
-	go.opentelemetry.io/otel/sdk v1.33.0 // indirect
+	go.opentelemetry.io/otel v1.34.0 // indirect
+	go.opentelemetry.io/otel/sdk v1.34.0 // indirect
 	golang.org/x/net v0.34.0 // indirect
 	golang.org/x/sys v0.29.0 // indirect
 	golang.org/x/text v0.21.0 // indirect
-	google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d // indirect
+	google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
 )
 
diff --git a/api/go.sum b/api/go.sum
index d0c057fc4..6fd065bd7 100644
--- a/api/go.sum
+++ b/api/go.sum
@@ -14,8 +14,8 @@ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
 github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
 github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 h1:VNqngBF40hVlDloBruUehVYC3ArSgIyScOAyMRqBxRg=
-github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1/go.mod h1:RBRO7fro65R6tjKzYgLAFo0t1QEXY1Dp+i/bvpRiqiQ=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.0 h1:VD1gqscl4nYs1YxVuSdemTrSgTKrwOWDK0FVFMqm+Cg=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.0/go.mod h1:4EgsQoS4TOhJizV+JTFg40qx1Ofh3XmXEQNBpgvNT40=
 github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
 github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
 github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
@@ -32,16 +32,16 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
 github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
 go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
-go.opentelemetry.io/otel v1.33.0 h1:/FerN9bax5LoK51X/sI0SVYrjSE0/yUL7DpxW4K3FWw=
-go.opentelemetry.io/otel v1.33.0/go.mod h1:SUUkR6csvUQl+yjReHu5uM3EtVV7MBm5FHKRlNx4I8I=
-go.opentelemetry.io/otel/metric v1.33.0 h1:r+JOocAyeRVXD8lZpjdQjzMadVZp2M4WmQ+5WtEnklQ=
-go.opentelemetry.io/otel/metric v1.33.0/go.mod h1:L9+Fyctbp6HFTddIxClbQkjtubW6O9QS3Ann/M82u6M=
-go.opentelemetry.io/otel/sdk v1.33.0 h1:iax7M131HuAm9QkZotNHEfstof92xM+N8sr3uHXc2IM=
-go.opentelemetry.io/otel/sdk v1.33.0/go.mod h1:A1Q5oi7/9XaMlIWzPSxLRWOI8nG3FnzHJNbiENQuihM=
-go.opentelemetry.io/otel/sdk/metric v1.31.0 h1:i9hxxLJF/9kkvfHppyLL55aW7iIJz4JjxTeYusH7zMc=
-go.opentelemetry.io/otel/sdk/metric v1.31.0/go.mod h1:CRInTMVvNhUKgSAMbKyTMxqOBC0zgyxzW55lZzX43Y8=
-go.opentelemetry.io/otel/trace v1.33.0 h1:cCJuF7LRjUFso9LPnEAHJDB2pqzp+hbO8eu1qqW2d/s=
-go.opentelemetry.io/otel/trace v1.33.0/go.mod h1:uIcdVUZMpTAmz0tI1z04GoVSezK37CbGV4fr1f2nBck=
+go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY=
+go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI=
+go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ=
+go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE=
+go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A=
+go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU=
+go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU=
+go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ=
+go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k=
+go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
@@ -73,14 +73,14 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-google.golang.org/genproto/googleapis/api v0.0.0-20250102185135-69823020774d h1:H8tOf8XM88HvKqLTxe755haY6r1fqqzLbEnfrmLXlSA=
-google.golang.org/genproto/googleapis/api v0.0.0-20250102185135-69823020774d/go.mod h1:2v7Z7gP2ZUOGsaFyxATQSRoBnKygqVq2Cwnvom7QiqY=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d h1:xJJRGY7TJcvIlpSrN3K6LAWgNFUILlO+OMAqtg9aqnw=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d/go.mod h1:3ENsm/5D1mzDyhpzeRi1NR784I0BcofWBoSc5QqqMK4=
-google.golang.org/grpc v1.69.4 h1:MF5TftSMkd8GLw/m0KM6V8CMOCY6NZ1NQDPGFgbTt4A=
-google.golang.org/grpc v1.69.4/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4=
-google.golang.org/protobuf v1.36.2 h1:R8FeyR1/eLmkutZOM5CWghmo5itiG9z0ktFlTVLuTmU=
-google.golang.org/protobuf v1.36.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
+google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f h1:gap6+3Gk41EItBuyi4XX/bp4oqJ3UwuIMl25yGinuAA=
+google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:Ic02D47M+zbarjYYUlK57y316f2MoN0gjAwI3f2S95o=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f h1:OxYkA3wjPsZyBylwymxSHa7ViiW1Sml4ToBrncvFehI=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:+2Yz8+CLJbIfL9z73EW45avw8Lmge3xVElCP9zEKi50=
+google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ=
+google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw=
+google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM=
+google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
diff --git a/bill-of-materials.json b/bill-of-materials.json
index 3158356aa..16fd5131f 100644
--- a/bill-of-materials.json
+++ b/bill-of-materials.json
@@ -144,7 +144,7 @@
 		]
 	},
 	{
-		"project": "github.com/golang-jwt/jwt/v4",
+		"project": "github.com/golang-jwt/jwt/v5",
 		"licenses": [
 			{
 				"type": "MIT License",
@@ -216,7 +216,16 @@
 		]
 	},
 	{
-		"project": "github.com/grpc-ecosystem/go-grpc-prometheus",
+		"project": "github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus",
+		"licenses": [
+			{
+				"type": "Apache License 2.0",
+				"confidence": 1
+			}
+		]
+	},
+	{
+		"project": "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors",
 		"licenses": [
 			{
 				"type": "Apache License 2.0",
@@ -785,6 +794,10 @@
 	{
 		"project": "sigs.k8s.io/yaml",
 		"licenses": [
+			{
+				"type": "Apache License 2.0",
+				"confidence": 1
+			},
 			{
 				"type": "BSD 3-clause \"New\" or \"Revised\" License",
 				"confidence": 1
diff --git a/bill-of-materials.override.json b/bill-of-materials.override.json
index 15afc5640..9d8d2e9bc 100644
--- a/bill-of-materials.override.json
+++ b/bill-of-materials.override.json
@@ -7,6 +7,15 @@
 			}
 		]
 	},
+	{
+		"project": "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors",
+		"licenses": [
+			{
+				"type": "Apache License 2.0",
+				"confidence": 1
+			}
+		]
+	},
 	{
 		"project": "github.com/inconshreveable/mousetrap",
 		"licenses": [
diff --git a/client/v3/go.mod b/client/v3/go.mod
index 89af9cc3a..0aebfba0b 100644
--- a/client/v3/go.mod
+++ b/client/v3/go.mod
@@ -7,13 +7,13 @@ toolchain go1.23.5
 require (
 	github.com/coreos/go-semver v0.3.1
 	github.com/dustin/go-humanize v1.0.1
-	github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0
+	github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1
 	github.com/prometheus/client_golang v1.20.5
 	github.com/stretchr/testify v1.10.0
 	go.etcd.io/etcd/api/v3 v3.6.0-alpha.0
 	go.etcd.io/etcd/client/pkg/v3 v3.6.0-alpha.0
 	go.uber.org/zap v1.27.0
-	google.golang.org/grpc v1.69.4
+	google.golang.org/grpc v1.70.0
 	sigs.k8s.io/yaml v1.4.0
 )
 
@@ -24,22 +24,23 @@ require (
 	github.com/davecgh/go-spew v1.1.1 // indirect
 	github.com/gogo/protobuf v1.3.2 // indirect
 	github.com/golang/protobuf v1.5.4 // indirect
-	github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 // indirect
+	github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 // indirect
+	github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.0 // indirect
 	github.com/klauspost/compress v1.17.9 // indirect
 	github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
 	github.com/pmezard/go-difflib v1.0.0 // indirect
 	github.com/prometheus/client_model v0.6.1 // indirect
-	github.com/prometheus/common v0.61.0 // indirect
+	github.com/prometheus/common v0.62.0 // indirect
 	github.com/prometheus/procfs v0.15.1 // indirect
-	go.opentelemetry.io/otel/metric v1.33.0 // indirect
-	go.opentelemetry.io/otel/trace v1.33.0 // indirect
+	go.opentelemetry.io/otel/metric v1.34.0 // indirect
+	go.opentelemetry.io/otel/trace v1.34.0 // indirect
 	go.uber.org/multierr v1.11.0 // indirect
 	golang.org/x/net v0.34.0 // indirect
 	golang.org/x/sys v0.29.0 // indirect
 	golang.org/x/text v0.21.0 // indirect
-	google.golang.org/genproto/googleapis/api v0.0.0-20250102185135-69823020774d // indirect
-	google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d // indirect
-	google.golang.org/protobuf v1.36.2 // indirect
+	google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f // indirect
+	google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect
+	google.golang.org/protobuf v1.36.4 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
 )
 
diff --git a/client/v3/go.sum b/client/v3/go.sum
index de26b4cb2..82fce51ce 100644
--- a/client/v3/go.sum
+++ b/client/v3/go.sum
@@ -24,10 +24,12 @@ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
 github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
 github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho=
-github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
-github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 h1:VNqngBF40hVlDloBruUehVYC3ArSgIyScOAyMRqBxRg=
-github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1/go.mod h1:RBRO7fro65R6tjKzYgLAFo0t1QEXY1Dp+i/bvpRiqiQ=
+github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 h1:qnpSQwGEnkcRpTqNOIR6bJbR0gAorgP9CSALpRcKoAA=
+github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1/go.mod h1:lXGCsh6c22WGtjr+qGHj1otzZpV/1kwTMAqkwZsnWRU=
+github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 h1:pRhl55Yx1eC7BZ1N+BBWwnKaMyD8uC+34TLdndZMAKk=
+github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0/go.mod h1:XKMd7iuf/RGPSMJ/U4HP0zS2Z9Fh8Ps9a+6X26m/tmI=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.0 h1:VD1gqscl4nYs1YxVuSdemTrSgTKrwOWDK0FVFMqm+Cg=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.0/go.mod h1:4EgsQoS4TOhJizV+JTFg40qx1Ofh3XmXEQNBpgvNT40=
 github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
 github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
 github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
@@ -46,8 +48,8 @@ github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+
 github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
 github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
 github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
-github.com/prometheus/common v0.61.0 h1:3gv/GThfX0cV2lpO7gkTUwZru38mxevy90Bj8YFSRQQ=
-github.com/prometheus/common v0.61.0/go.mod h1:zr29OCN/2BsJRaFwG8QOBr41D6kkchKbpeNH7pAjb/s=
+github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
+github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
 github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
 github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
 github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
@@ -58,16 +60,16 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
 github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
 go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
-go.opentelemetry.io/otel v1.33.0 h1:/FerN9bax5LoK51X/sI0SVYrjSE0/yUL7DpxW4K3FWw=
-go.opentelemetry.io/otel v1.33.0/go.mod h1:SUUkR6csvUQl+yjReHu5uM3EtVV7MBm5FHKRlNx4I8I=
-go.opentelemetry.io/otel/metric v1.33.0 h1:r+JOocAyeRVXD8lZpjdQjzMadVZp2M4WmQ+5WtEnklQ=
-go.opentelemetry.io/otel/metric v1.33.0/go.mod h1:L9+Fyctbp6HFTddIxClbQkjtubW6O9QS3Ann/M82u6M=
-go.opentelemetry.io/otel/sdk v1.33.0 h1:iax7M131HuAm9QkZotNHEfstof92xM+N8sr3uHXc2IM=
-go.opentelemetry.io/otel/sdk v1.33.0/go.mod h1:A1Q5oi7/9XaMlIWzPSxLRWOI8nG3FnzHJNbiENQuihM=
-go.opentelemetry.io/otel/sdk/metric v1.31.0 h1:i9hxxLJF/9kkvfHppyLL55aW7iIJz4JjxTeYusH7zMc=
-go.opentelemetry.io/otel/sdk/metric v1.31.0/go.mod h1:CRInTMVvNhUKgSAMbKyTMxqOBC0zgyxzW55lZzX43Y8=
-go.opentelemetry.io/otel/trace v1.33.0 h1:cCJuF7LRjUFso9LPnEAHJDB2pqzp+hbO8eu1qqW2d/s=
-go.opentelemetry.io/otel/trace v1.33.0/go.mod h1:uIcdVUZMpTAmz0tI1z04GoVSezK37CbGV4fr1f2nBck=
+go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY=
+go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI=
+go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ=
+go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE=
+go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A=
+go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU=
+go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU=
+go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ=
+go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k=
+go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE=
 go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
 go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
 go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
@@ -105,14 +107,14 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-google.golang.org/genproto/googleapis/api v0.0.0-20250102185135-69823020774d h1:H8tOf8XM88HvKqLTxe755haY6r1fqqzLbEnfrmLXlSA=
-google.golang.org/genproto/googleapis/api v0.0.0-20250102185135-69823020774d/go.mod h1:2v7Z7gP2ZUOGsaFyxATQSRoBnKygqVq2Cwnvom7QiqY=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d h1:xJJRGY7TJcvIlpSrN3K6LAWgNFUILlO+OMAqtg9aqnw=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d/go.mod h1:3ENsm/5D1mzDyhpzeRi1NR784I0BcofWBoSc5QqqMK4=
-google.golang.org/grpc v1.69.4 h1:MF5TftSMkd8GLw/m0KM6V8CMOCY6NZ1NQDPGFgbTt4A=
-google.golang.org/grpc v1.69.4/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4=
-google.golang.org/protobuf v1.36.2 h1:R8FeyR1/eLmkutZOM5CWghmo5itiG9z0ktFlTVLuTmU=
-google.golang.org/protobuf v1.36.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
+google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f h1:gap6+3Gk41EItBuyi4XX/bp4oqJ3UwuIMl25yGinuAA=
+google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:Ic02D47M+zbarjYYUlK57y316f2MoN0gjAwI3f2S95o=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f h1:OxYkA3wjPsZyBylwymxSHa7ViiW1Sml4ToBrncvFehI=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:+2Yz8+CLJbIfL9z73EW45avw8Lmge3xVElCP9zEKi50=
+google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ=
+google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw=
+google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM=
+google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
diff --git a/client/v3/kubernetes/interface.go b/client/v3/kubernetes/interface.go
index 19b82a629..3ffecc86a 100644
--- a/client/v3/kubernetes/interface.go
+++ b/client/v3/kubernetes/interface.go
@@ -36,8 +36,9 @@ type Interface interface {
 	// If opts.Revision is non-zero, the key-value pairs are retrieved at the specified revision.
 	// If the required revision has been compacted, the request will fail with ErrCompacted.
 	// If opts.Limit is greater than zero, the number of returned key-value pairs is bounded by the limit.
-	// If opts.Continue is not empty, the listing will start from the key immediately after the one specified by Continue.
-	// The Continue value should be the last key returned in a previous paginated ListResponse.
+	// If opts.Continue is not empty, the listing will start from the key
+	// specified by it. When paginating, the Continue value should be set
+	// to the last observed key with "\x00" appended to it.
 	List(ctx context.Context, prefix string, opts ListOptions) (ListResponse, error)
 
 	// Count returns the number of keys with the specified prefix.
@@ -74,8 +75,9 @@ type ListOptions struct {
 	// 0 means no limitation.
 	Limit int64
 
-	// Continue is a key from which to resume the List operation, excluding the given key.
-	// It should be set to the last key from a previous ListResponse when paginating.
+	// Continue is a key from which to resume the List operation.
+	// It should be set to the last key from a previous ListResponse
+	// with "\x00" appended to it when paginating.
 	Continue string
 }
 
diff --git a/contrib/lock/client/client.go b/contrib/lock/client/client.go
index ddb5498e5..19cb763f3 100644
--- a/contrib/lock/client/client.go
+++ b/contrib/lock/client/client.go
@@ -135,11 +135,10 @@ func main() {
 
 	err = write("key0", fmt.Sprintf("value from client %x", mode), int64(version))
 	if err != nil {
-		if mode == 1 {
-			log.Printf("expected fail to write to storage with old lease version: %s\n", err) // client 1 should show this message
-		} else {
+		if mode != 1 {
 			log.Fatalf("unexpected fail to write to storage: %s\n", err)
 		}
+		log.Printf("expected fail to write to storage with old lease version: %s\n", err) // client 1 should show this message
 	} else {
 		log.Printf("successfully write a key to storage using lease %x\n", int64(version))
 	}
diff --git a/contrib/mixin/alerts/alerts.libsonnet b/contrib/mixin/alerts/alerts.libsonnet
index 856fe4c1e..0a41632e6 100644
--- a/contrib/mixin/alerts/alerts.libsonnet
+++ b/contrib/mixin/alerts/alerts.libsonnet
@@ -16,9 +16,9 @@
               )
               > 0
             ||| % { etcd_instance_labels: $._config.etcd_instance_labels, etcd_selector: $._config.etcd_selector, network_failure_range: $._config.scrape_interval_seconds * 4 },
-            'for': '10m',
+            'for': '20m',
             labels: {
-              severity: 'critical',
+              severity: 'warning',
             },
             annotations: {
               description: 'etcd cluster "{{ $labels.%s }}": members are down ({{ $value }}).' % $._config.clusterLabel,
diff --git a/contrib/mixin/test.yaml b/contrib/mixin/test.yaml
index bfb50d8af..718eeba7a 100644
--- a/contrib/mixin/test.yaml
+++ b/contrib/mixin/test.yaml
@@ -5,24 +5,24 @@ tests:
   - interval: 1m
     input_series:
       - series: up{job="etcd",instance="10.10.10.0"}
-        values: 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0
+        values: 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0
       - series: up{job="etcd",instance="10.10.10.1"}
-        values: 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0
+        values: 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
       - series: up{job="etcd",instance="10.10.10.2"}
-        values: 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0
+        values: 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
     alert_rule_test:
       - eval_time: 3m
         alertname: etcdInsufficientMembers
       - eval_time: 5m
         alertname: etcdInsufficientMembers
-      - eval_time: 12m
+      - eval_time: 22m
         alertname: etcdMembersDown
-      - eval_time: 14m
+      - eval_time: 24m
         alertname: etcdMembersDown
         exp_alerts:
           - exp_labels:
               job: etcd
-              severity: critical
+              severity: warning
             exp_annotations:
               description: 'etcd cluster "etcd": members are down (3).'
               summary: etcd cluster members are down.
@@ -55,30 +55,30 @@ tests:
       - series: up{job="etcd",instance="10.10.10.2"}
         values: 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
     alert_rule_test:
-      - eval_time: 14m
+      - eval_time: 24m
         alertname: etcdMembersDown
         exp_alerts:
           - exp_labels:
               job: etcd
-              severity: critical
+              severity: warning
             exp_annotations:
               description: 'etcd cluster "etcd": members are down (3).'
               summary: etcd cluster members are down.
   - interval: 1m
     input_series:
       - series: up{job="etcd",instance="10.10.10.0"}
-        values: 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0
+        values: 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0
       - series: up{job="etcd",instance="10.10.10.1"}
-        values: 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0
+        values: 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0
       - series: etcd_network_peer_sent_failures_total{To="member-1",job="etcd",endpoint="test"}
-        values: 0 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
+        values: 0 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
     alert_rule_test:
-      - eval_time: 13m
+      - eval_time: 23m
         alertname: etcdMembersDown
         exp_alerts:
           - exp_labels:
               job: etcd
-              severity: critical
+              severity: warning
             exp_annotations:
               description: 'etcd cluster "etcd": members are down (1).'
               summary: etcd cluster members are down.
diff --git a/etcdctl/README.md b/etcdctl/README.md
index 40e93c4d1..3589f4d69 100644
--- a/etcdctl/README.md
+++ b/etcdctl/README.md
@@ -1119,7 +1119,7 @@ DOWNGRADE ENABLE starts a downgrade action to cluster.
 Downgrade enable success, cluster version 3.6
 ```
 
-### DOWNGRADE CANCEL \<TARGET_VERSION\>
+### DOWNGRADE CANCEL
 
 DOWNGRADE CANCEL cancels the ongoing downgrade action to cluster.
 
diff --git a/etcdctl/ctlv3/command/check.go b/etcdctl/ctlv3/command/check.go
index 3678f32c0..4626d5222 100644
--- a/etcdctl/ctlv3/command/check.go
+++ b/etcdctl/ctlv3/command/check.go
@@ -257,12 +257,11 @@ func newCheckPerfCommand(cmd *cobra.Command, args []string) {
 		fmt.Printf("PASS: Stddev is %fs\n", s.Stddev)
 	}
 
-	if ok {
-		fmt.Println("PASS")
-	} else {
+	if !ok {
 		fmt.Println("FAIL")
 		os.Exit(cobrautl.ExitError)
 	}
+	fmt.Println("PASS")
 }
 
 func attemptCleanup(client *v3.Client, autoCompact bool) {
@@ -434,7 +433,6 @@ func newCheckDatascaleCommand(cmd *cobra.Command, args []string) {
 			fmt.Printf("FAIL: ERROR(%v) -> %d\n", k, v)
 		}
 		os.Exit(cobrautl.ExitError)
-	} else {
-		fmt.Printf("PASS: Approximate system memory used : %v MB.\n", strconv.FormatFloat(mbUsed, 'f', 2, 64))
 	}
+	fmt.Printf("PASS: Approximate system memory used : %v MB.\n", strconv.FormatFloat(mbUsed, 'f', 2, 64))
 }
diff --git a/etcdctl/ctlv3/ctl.go b/etcdctl/ctlv3/ctl.go
index 168386fb0..6686940ce 100644
--- a/etcdctl/ctlv3/ctl.go
+++ b/etcdctl/ctlv3/ctl.go
@@ -116,9 +116,8 @@ func MustStart() {
 	if err := Start(); err != nil {
 		if rootCmd.SilenceErrors {
 			cobrautl.ExitWithError(cobrautl.ExitError, err)
-		} else {
-			os.Exit(cobrautl.ExitError)
 		}
+		os.Exit(cobrautl.ExitError)
 	}
 }
 
diff --git a/etcdctl/go.mod b/etcdctl/go.mod
index e626657ad..663a7d253 100644
--- a/etcdctl/go.mod
+++ b/etcdctl/go.mod
@@ -6,7 +6,7 @@ toolchain go1.23.5
 
 require (
 	github.com/bgentry/speakeasy v0.2.0
-	github.com/cheggaaa/pb/v3 v3.1.5
+	github.com/cheggaaa/pb/v3 v3.1.6
 	github.com/dustin/go-humanize v1.0.1
 	github.com/olekukonko/tablewriter v0.0.5
 	github.com/spf13/cobra v1.8.1
@@ -18,7 +18,7 @@ require (
 	go.etcd.io/etcd/pkg/v3 v3.6.0-alpha.0
 	go.uber.org/zap v1.27.0
 	golang.org/x/time v0.9.0
-	google.golang.org/grpc v1.69.4
+	google.golang.org/grpc v1.70.0
 )
 
 require (
@@ -29,9 +29,9 @@ require (
 	github.com/fatih/color v1.18.0 // indirect
 	github.com/gogo/protobuf v1.3.2 // indirect
 	github.com/golang/protobuf v1.5.4 // indirect
-	github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 // indirect
+	github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.0 // indirect
 	github.com/inconshreveable/mousetrap v1.1.0 // indirect
-	github.com/mattn/go-colorable v0.1.13 // indirect
+	github.com/mattn/go-colorable v0.1.14 // indirect
 	github.com/mattn/go-isatty v0.0.20 // indirect
 	github.com/mattn/go-runewidth v0.0.16 // indirect
 	github.com/pmezard/go-difflib v1.0.0 // indirect
@@ -41,9 +41,9 @@ require (
 	golang.org/x/net v0.34.0 // indirect
 	golang.org/x/sys v0.29.0 // indirect
 	golang.org/x/text v0.21.0 // indirect
-	google.golang.org/genproto/googleapis/api v0.0.0-20250102185135-69823020774d // indirect
-	google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d // indirect
-	google.golang.org/protobuf v1.36.2 // indirect
+	google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f // indirect
+	google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect
+	google.golang.org/protobuf v1.36.4 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
 )
 
diff --git a/etcdctl/go.sum b/etcdctl/go.sum
index bacf773e7..956c83bef 100644
--- a/etcdctl/go.sum
+++ b/etcdctl/go.sum
@@ -6,8 +6,8 @@ github.com/bgentry/speakeasy v0.2.0 h1:tgObeVOf8WAvtuAX6DhJ4xks4CFNwPDZiqzGqIHE5
 github.com/bgentry/speakeasy v0.2.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
 github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
 github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
-github.com/cheggaaa/pb/v3 v3.1.5 h1:QuuUzeM2WsAqG2gMqtzaWithDJv0i+i6UlnwSCI4QLk=
-github.com/cheggaaa/pb/v3 v3.1.5/go.mod h1:CrxkeghYTXi1lQBEI7jSn+3svI3cuc19haAj6jM60XI=
+github.com/cheggaaa/pb/v3 v3.1.6 h1:h0x+vd7EiUohAJ29DJtJy+SNAc55t/elW3jCD086EXk=
+github.com/cheggaaa/pb/v3 v3.1.6/go.mod h1:urxmfVtaxT+9aWk92DbsvXFZtNSWQSO5TRAp+MJ3l1s=
 github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4=
 github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec=
 github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
@@ -32,10 +32,12 @@ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
 github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
 github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho=
-github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
-github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 h1:VNqngBF40hVlDloBruUehVYC3ArSgIyScOAyMRqBxRg=
-github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1/go.mod h1:RBRO7fro65R6tjKzYgLAFo0t1QEXY1Dp+i/bvpRiqiQ=
+github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 h1:qnpSQwGEnkcRpTqNOIR6bJbR0gAorgP9CSALpRcKoAA=
+github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1/go.mod h1:lXGCsh6c22WGtjr+qGHj1otzZpV/1kwTMAqkwZsnWRU=
+github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 h1:pRhl55Yx1eC7BZ1N+BBWwnKaMyD8uC+34TLdndZMAKk=
+github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0/go.mod h1:XKMd7iuf/RGPSMJ/U4HP0zS2Z9Fh8Ps9a+6X26m/tmI=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.0 h1:VD1gqscl4nYs1YxVuSdemTrSgTKrwOWDK0FVFMqm+Cg=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.0/go.mod h1:4EgsQoS4TOhJizV+JTFg40qx1Ofh3XmXEQNBpgvNT40=
 github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
 github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
 github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
@@ -46,9 +48,8 @@ github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
 github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
 github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
 github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
-github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
-github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
-github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
+github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
+github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
 github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
 github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
 github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
@@ -64,8 +65,8 @@ github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+
 github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
 github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
 github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
-github.com/prometheus/common v0.61.0 h1:3gv/GThfX0cV2lpO7gkTUwZru38mxevy90Bj8YFSRQQ=
-github.com/prometheus/common v0.61.0/go.mod h1:zr29OCN/2BsJRaFwG8QOBr41D6kkchKbpeNH7pAjb/s=
+github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
+github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
 github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
 github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
 github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
@@ -84,16 +85,16 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
 github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
 go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
-go.opentelemetry.io/otel v1.33.0 h1:/FerN9bax5LoK51X/sI0SVYrjSE0/yUL7DpxW4K3FWw=
-go.opentelemetry.io/otel v1.33.0/go.mod h1:SUUkR6csvUQl+yjReHu5uM3EtVV7MBm5FHKRlNx4I8I=
-go.opentelemetry.io/otel/metric v1.33.0 h1:r+JOocAyeRVXD8lZpjdQjzMadVZp2M4WmQ+5WtEnklQ=
-go.opentelemetry.io/otel/metric v1.33.0/go.mod h1:L9+Fyctbp6HFTddIxClbQkjtubW6O9QS3Ann/M82u6M=
-go.opentelemetry.io/otel/sdk v1.33.0 h1:iax7M131HuAm9QkZotNHEfstof92xM+N8sr3uHXc2IM=
-go.opentelemetry.io/otel/sdk v1.33.0/go.mod h1:A1Q5oi7/9XaMlIWzPSxLRWOI8nG3FnzHJNbiENQuihM=
-go.opentelemetry.io/otel/sdk/metric v1.31.0 h1:i9hxxLJF/9kkvfHppyLL55aW7iIJz4JjxTeYusH7zMc=
-go.opentelemetry.io/otel/sdk/metric v1.31.0/go.mod h1:CRInTMVvNhUKgSAMbKyTMxqOBC0zgyxzW55lZzX43Y8=
-go.opentelemetry.io/otel/trace v1.33.0 h1:cCJuF7LRjUFso9LPnEAHJDB2pqzp+hbO8eu1qqW2d/s=
-go.opentelemetry.io/otel/trace v1.33.0/go.mod h1:uIcdVUZMpTAmz0tI1z04GoVSezK37CbGV4fr1f2nBck=
+go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY=
+go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI=
+go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ=
+go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE=
+go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A=
+go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU=
+go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU=
+go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ=
+go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k=
+go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE=
 go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
 go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
 go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
@@ -117,7 +118,6 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
 golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
@@ -135,14 +135,14 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-google.golang.org/genproto/googleapis/api v0.0.0-20250102185135-69823020774d h1:H8tOf8XM88HvKqLTxe755haY6r1fqqzLbEnfrmLXlSA=
-google.golang.org/genproto/googleapis/api v0.0.0-20250102185135-69823020774d/go.mod h1:2v7Z7gP2ZUOGsaFyxATQSRoBnKygqVq2Cwnvom7QiqY=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d h1:xJJRGY7TJcvIlpSrN3K6LAWgNFUILlO+OMAqtg9aqnw=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d/go.mod h1:3ENsm/5D1mzDyhpzeRi1NR784I0BcofWBoSc5QqqMK4=
-google.golang.org/grpc v1.69.4 h1:MF5TftSMkd8GLw/m0KM6V8CMOCY6NZ1NQDPGFgbTt4A=
-google.golang.org/grpc v1.69.4/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4=
-google.golang.org/protobuf v1.36.2 h1:R8FeyR1/eLmkutZOM5CWghmo5itiG9z0ktFlTVLuTmU=
-google.golang.org/protobuf v1.36.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
+google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f h1:gap6+3Gk41EItBuyi4XX/bp4oqJ3UwuIMl25yGinuAA=
+google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:Ic02D47M+zbarjYYUlK57y316f2MoN0gjAwI3f2S95o=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f h1:OxYkA3wjPsZyBylwymxSHa7ViiW1Sml4ToBrncvFehI=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:+2Yz8+CLJbIfL9z73EW45avw8Lmge3xVElCP9zEKi50=
+google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ=
+google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw=
+google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM=
+google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
diff --git a/etcdutl/go.mod b/etcdutl/go.mod
index 4ab611ef6..e758d1d08 100644
--- a/etcdutl/go.mod
+++ b/etcdutl/go.mod
@@ -46,14 +46,15 @@ require (
 	github.com/go-logr/logr v1.4.2 // indirect
 	github.com/go-logr/stdr v1.2.2 // indirect
 	github.com/gogo/protobuf v1.3.2 // indirect
-	github.com/golang-jwt/jwt/v4 v4.5.1 // indirect
+	github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
 	github.com/golang/protobuf v1.5.4 // indirect
 	github.com/google/btree v1.1.3 // indirect
 	github.com/google/go-cmp v0.6.0 // indirect
 	github.com/google/uuid v1.6.0 // indirect
 	github.com/gorilla/websocket v1.4.2 // indirect
-	github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect
-	github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 // indirect
+	github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 // indirect
+	github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 // indirect
+	github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.0 // indirect
 	github.com/inconshreveable/mousetrap v1.1.0 // indirect
 	github.com/jonboulle/clockwork v0.5.0 // indirect
 	github.com/klauspost/compress v1.17.9 // indirect
@@ -62,7 +63,7 @@ require (
 	github.com/pmezard/go-difflib v1.0.0 // indirect
 	github.com/prometheus/client_golang v1.20.5 // indirect
 	github.com/prometheus/client_model v0.6.1 // indirect
-	github.com/prometheus/common v0.61.0 // indirect
+	github.com/prometheus/common v0.62.0 // indirect
 	github.com/prometheus/procfs v0.15.1 // indirect
 	github.com/rivo/uniseg v0.4.7 // indirect
 	github.com/sirupsen/logrus v1.9.3 // indirect
@@ -72,13 +73,13 @@ require (
 	github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 // indirect
 	go.etcd.io/etcd/client/v2 v2.306.0-alpha.0 // indirect
 	go.opentelemetry.io/auto/sdk v1.1.0 // indirect
-	go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.58.0 // indirect
-	go.opentelemetry.io/otel v1.33.0 // indirect
-	go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0 // indirect
-	go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0 // indirect
-	go.opentelemetry.io/otel/metric v1.33.0 // indirect
-	go.opentelemetry.io/otel/sdk v1.33.0 // indirect
-	go.opentelemetry.io/otel/trace v1.33.0 // indirect
+	go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0 // indirect
+	go.opentelemetry.io/otel v1.34.0 // indirect
+	go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 // indirect
+	go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 // indirect
+	go.opentelemetry.io/otel/metric v1.34.0 // indirect
+	go.opentelemetry.io/otel/sdk v1.34.0 // indirect
+	go.opentelemetry.io/otel/trace v1.34.0 // indirect
 	go.opentelemetry.io/proto/otlp v1.5.0 // indirect
 	go.uber.org/multierr v1.11.0 // indirect
 	golang.org/x/crypto v0.32.0 // indirect
@@ -86,10 +87,10 @@ require (
 	golang.org/x/sys v0.29.0 // indirect
 	golang.org/x/text v0.21.0 // indirect
 	golang.org/x/time v0.9.0 // indirect
-	google.golang.org/genproto/googleapis/api v0.0.0-20250102185135-69823020774d // indirect
-	google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d // indirect
-	google.golang.org/grpc v1.69.4 // indirect
-	google.golang.org/protobuf v1.36.2 // indirect
+	google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f // indirect
+	google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect
+	google.golang.org/grpc v1.70.0 // indirect
+	google.golang.org/protobuf v1.36.4 // indirect
 	gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
 	sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6 // indirect
diff --git a/etcdutl/go.sum b/etcdutl/go.sum
index 16c263658..326f5180c 100644
--- a/etcdutl/go.sum
+++ b/etcdutl/go.sum
@@ -24,8 +24,8 @@ github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre
 github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
 github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
 github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
-github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo=
-github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
+github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
+github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
 github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
 github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
 github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
@@ -37,10 +37,12 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
 github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
 github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
-github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho=
-github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
-github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 h1:VNqngBF40hVlDloBruUehVYC3ArSgIyScOAyMRqBxRg=
-github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1/go.mod h1:RBRO7fro65R6tjKzYgLAFo0t1QEXY1Dp+i/bvpRiqiQ=
+github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 h1:qnpSQwGEnkcRpTqNOIR6bJbR0gAorgP9CSALpRcKoAA=
+github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1/go.mod h1:lXGCsh6c22WGtjr+qGHj1otzZpV/1kwTMAqkwZsnWRU=
+github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 h1:pRhl55Yx1eC7BZ1N+BBWwnKaMyD8uC+34TLdndZMAKk=
+github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0/go.mod h1:XKMd7iuf/RGPSMJ/U4HP0zS2Z9Fh8Ps9a+6X26m/tmI=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.0 h1:VD1gqscl4nYs1YxVuSdemTrSgTKrwOWDK0FVFMqm+Cg=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.0/go.mod h1:4EgsQoS4TOhJizV+JTFg40qx1Ofh3XmXEQNBpgvNT40=
 github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
 github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
 github.com/jonboulle/clockwork v0.5.0 h1:Hyh9A8u51kptdkR+cqRpT1EebBwTn1oK9YfGYbdFz6I=
@@ -68,8 +70,8 @@ github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+
 github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
 github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
 github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
-github.com/prometheus/common v0.61.0 h1:3gv/GThfX0cV2lpO7gkTUwZru38mxevy90Bj8YFSRQQ=
-github.com/prometheus/common v0.61.0/go.mod h1:zr29OCN/2BsJRaFwG8QOBr41D6kkchKbpeNH7pAjb/s=
+github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
+github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
 github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
 github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
 github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
@@ -102,22 +104,22 @@ go.etcd.io/raft/v3 v3.6.0-beta.0 h1:MZFQVjCQxPJj5K9oS69Y+atNvYnGNyOQBnroTdw56jQ=
 go.etcd.io/raft/v3 v3.6.0-beta.0/go.mod h1:C2JoekRXfvImSrk5GnqD0aZ3a+cGVRnyem9qqn2DCEw=
 go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
 go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
-go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.58.0 h1:PS8wXpbyaDJQ2VDHHncMe9Vct0Zn1fEjpsjrLxGJoSc=
-go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.58.0/go.mod h1:HDBUsEjOuRC0EzKZ1bSaRGZWUBAzo+MhAcUUORSr4D0=
-go.opentelemetry.io/otel v1.33.0 h1:/FerN9bax5LoK51X/sI0SVYrjSE0/yUL7DpxW4K3FWw=
-go.opentelemetry.io/otel v1.33.0/go.mod h1:SUUkR6csvUQl+yjReHu5uM3EtVV7MBm5FHKRlNx4I8I=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0 h1:Vh5HayB/0HHfOQA7Ctx69E/Y/DcQSMPpKANYVMQ7fBA=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0/go.mod h1:cpgtDBaqD/6ok/UG0jT15/uKjAY8mRA53diogHBg3UI=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0 h1:5pojmb1U1AogINhN3SurB+zm/nIcusopeBNp42f45QM=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0/go.mod h1:57gTHJSE5S1tqg+EKsLPlTWhpHMsWlVmer+LA926XiA=
-go.opentelemetry.io/otel/metric v1.33.0 h1:r+JOocAyeRVXD8lZpjdQjzMadVZp2M4WmQ+5WtEnklQ=
-go.opentelemetry.io/otel/metric v1.33.0/go.mod h1:L9+Fyctbp6HFTddIxClbQkjtubW6O9QS3Ann/M82u6M=
-go.opentelemetry.io/otel/sdk v1.33.0 h1:iax7M131HuAm9QkZotNHEfstof92xM+N8sr3uHXc2IM=
-go.opentelemetry.io/otel/sdk v1.33.0/go.mod h1:A1Q5oi7/9XaMlIWzPSxLRWOI8nG3FnzHJNbiENQuihM=
-go.opentelemetry.io/otel/sdk/metric v1.31.0 h1:i9hxxLJF/9kkvfHppyLL55aW7iIJz4JjxTeYusH7zMc=
-go.opentelemetry.io/otel/sdk/metric v1.31.0/go.mod h1:CRInTMVvNhUKgSAMbKyTMxqOBC0zgyxzW55lZzX43Y8=
-go.opentelemetry.io/otel/trace v1.33.0 h1:cCJuF7LRjUFso9LPnEAHJDB2pqzp+hbO8eu1qqW2d/s=
-go.opentelemetry.io/otel/trace v1.33.0/go.mod h1:uIcdVUZMpTAmz0tI1z04GoVSezK37CbGV4fr1f2nBck=
+go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0 h1:rgMkmiGfix9vFJDcDi1PK8WEQP4FLQwLDfhp5ZLpFeE=
+go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0/go.mod h1:ijPqXp5P6IRRByFVVg9DY8P5HkxkHE5ARIa+86aXPf4=
+go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY=
+go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 h1:OeNbIYk/2C15ckl7glBlOBp5+WlYsOElzTNmiPW/x60=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0/go.mod h1:7Bept48yIeqxP2OZ9/AqIpYS94h2or0aB4FypJTc8ZM=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 h1:tgJ0uaNS4c98WRNUEx5U3aDlrDOI5Rs+1Vifcw4DJ8U=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0/go.mod h1:U7HYyW0zt/a9x5J1Kjs+r1f/d4ZHnYFclhYY2+YbeoE=
+go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ=
+go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE=
+go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A=
+go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU=
+go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU=
+go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ=
+go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k=
+go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE=
 go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4=
 go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4=
 go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
@@ -165,14 +167,14 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-google.golang.org/genproto/googleapis/api v0.0.0-20250102185135-69823020774d h1:H8tOf8XM88HvKqLTxe755haY6r1fqqzLbEnfrmLXlSA=
-google.golang.org/genproto/googleapis/api v0.0.0-20250102185135-69823020774d/go.mod h1:2v7Z7gP2ZUOGsaFyxATQSRoBnKygqVq2Cwnvom7QiqY=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d h1:xJJRGY7TJcvIlpSrN3K6LAWgNFUILlO+OMAqtg9aqnw=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d/go.mod h1:3ENsm/5D1mzDyhpzeRi1NR784I0BcofWBoSc5QqqMK4=
-google.golang.org/grpc v1.69.4 h1:MF5TftSMkd8GLw/m0KM6V8CMOCY6NZ1NQDPGFgbTt4A=
-google.golang.org/grpc v1.69.4/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4=
-google.golang.org/protobuf v1.36.2 h1:R8FeyR1/eLmkutZOM5CWghmo5itiG9z0ktFlTVLuTmU=
-google.golang.org/protobuf v1.36.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
+google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f h1:gap6+3Gk41EItBuyi4XX/bp4oqJ3UwuIMl25yGinuAA=
+google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:Ic02D47M+zbarjYYUlK57y316f2MoN0gjAwI3f2S95o=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f h1:OxYkA3wjPsZyBylwymxSHa7ViiW1Sml4ToBrncvFehI=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:+2Yz8+CLJbIfL9z73EW45avw8Lmge3xVElCP9zEKi50=
+google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ=
+google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw=
+google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM=
+google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
diff --git a/go.mod b/go.mod
index 3999400c1..bad0af2a6 100644
--- a/go.mod
+++ b/go.mod
@@ -18,7 +18,7 @@ replace (
 
 require (
 	github.com/bgentry/speakeasy v0.2.0
-	github.com/cheggaaa/pb/v3 v3.1.5
+	github.com/cheggaaa/pb/v3 v3.1.6
 	github.com/coreos/go-semver v0.3.1
 	github.com/dustin/go-humanize v1.0.1
 	github.com/spf13/cobra v1.8.1
@@ -36,8 +36,8 @@ require (
 	go.etcd.io/raft/v3 v3.6.0-beta.0
 	go.uber.org/zap v1.27.0
 	golang.org/x/time v0.9.0
-	google.golang.org/grpc v1.69.4
-	google.golang.org/protobuf v1.36.2
+	google.golang.org/grpc v1.70.0
+	google.golang.org/protobuf v1.36.4
 )
 
 require (
@@ -51,7 +51,7 @@ require (
 	github.com/go-logr/logr v1.4.2 // indirect
 	github.com/go-logr/stdr v1.2.2 // indirect
 	github.com/gogo/protobuf v1.3.2 // indirect
-	github.com/golang-jwt/jwt/v4 v4.5.1 // indirect
+	github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
 	github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
 	github.com/golang/protobuf v1.5.4 // indirect
 	github.com/google/btree v1.1.3 // indirect
@@ -59,12 +59,13 @@ require (
 	github.com/google/uuid v1.6.0 // indirect
 	github.com/gorilla/websocket v1.4.2 // indirect
 	github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect
-	github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect
-	github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 // indirect
+	github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 // indirect
+	github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 // indirect
+	github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.0 // indirect
 	github.com/inconshreveable/mousetrap v1.1.0 // indirect
 	github.com/jonboulle/clockwork v0.5.0 // indirect
 	github.com/klauspost/compress v1.17.9 // indirect
-	github.com/mattn/go-colorable v0.1.13 // indirect
+	github.com/mattn/go-colorable v0.1.14 // indirect
 	github.com/mattn/go-isatty v0.0.20 // indirect
 	github.com/mattn/go-runewidth v0.0.16 // indirect
 	github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
@@ -72,7 +73,7 @@ require (
 	github.com/pmezard/go-difflib v1.0.0 // indirect
 	github.com/prometheus/client_golang v1.20.5 // indirect
 	github.com/prometheus/client_model v0.6.1 // indirect
-	github.com/prometheus/common v0.61.0 // indirect
+	github.com/prometheus/common v0.62.0 // indirect
 	github.com/prometheus/procfs v0.15.1 // indirect
 	github.com/rivo/uniseg v0.4.7 // indirect
 	github.com/sirupsen/logrus v1.9.3 // indirect
@@ -82,21 +83,21 @@ require (
 	github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 // indirect
 	go.etcd.io/gofail v0.2.0 // indirect
 	go.opentelemetry.io/auto/sdk v1.1.0 // indirect
-	go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.58.0 // indirect
-	go.opentelemetry.io/otel v1.33.0 // indirect
-	go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0 // indirect
-	go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0 // indirect
-	go.opentelemetry.io/otel/metric v1.33.0 // indirect
-	go.opentelemetry.io/otel/sdk v1.33.0 // indirect
-	go.opentelemetry.io/otel/trace v1.33.0 // indirect
+	go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0 // indirect
+	go.opentelemetry.io/otel v1.34.0 // indirect
+	go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 // indirect
+	go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 // indirect
+	go.opentelemetry.io/otel/metric v1.34.0 // indirect
+	go.opentelemetry.io/otel/sdk v1.34.0 // indirect
+	go.opentelemetry.io/otel/trace v1.34.0 // indirect
 	go.opentelemetry.io/proto/otlp v1.5.0 // indirect
 	go.uber.org/multierr v1.11.0 // indirect
 	golang.org/x/crypto v0.32.0 // indirect
 	golang.org/x/net v0.34.0 // indirect
 	golang.org/x/sys v0.29.0 // indirect
 	golang.org/x/text v0.21.0 // indirect
-	google.golang.org/genproto/googleapis/api v0.0.0-20250102185135-69823020774d // indirect
-	google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d // indirect
+	google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f // indirect
+	google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect
 	gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
 	sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6 // indirect
diff --git a/go.sum b/go.sum
index eb883a23e..77cd78df5 100644
--- a/go.sum
+++ b/go.sum
@@ -11,8 +11,8 @@ github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyY
 github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
 github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
 github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
-github.com/cheggaaa/pb/v3 v3.1.5 h1:QuuUzeM2WsAqG2gMqtzaWithDJv0i+i6UlnwSCI4QLk=
-github.com/cheggaaa/pb/v3 v3.1.5/go.mod h1:CrxkeghYTXi1lQBEI7jSn+3svI3cuc19haAj6jM60XI=
+github.com/cheggaaa/pb/v3 v3.1.6 h1:h0x+vd7EiUohAJ29DJtJy+SNAc55t/elW3jCD086EXk=
+github.com/cheggaaa/pb/v3 v3.1.6/go.mod h1:urxmfVtaxT+9aWk92DbsvXFZtNSWQSO5TRAp+MJ3l1s=
 github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
 github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
 github.com/cockroachdb/datadriven v1.0.2 h1:H9MtNqVoVhvd9nCBwOyDjUEdZCREqbIdCJD93PBm/jA=
@@ -44,8 +44,8 @@ github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/me
 github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
 github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
 github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
-github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo=
-github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
+github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
+github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
 github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
 github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@@ -67,10 +67,12 @@ github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0U
 github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
 github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw=
 github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y=
-github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho=
-github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
-github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 h1:VNqngBF40hVlDloBruUehVYC3ArSgIyScOAyMRqBxRg=
-github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1/go.mod h1:RBRO7fro65R6tjKzYgLAFo0t1QEXY1Dp+i/bvpRiqiQ=
+github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 h1:qnpSQwGEnkcRpTqNOIR6bJbR0gAorgP9CSALpRcKoAA=
+github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1/go.mod h1:lXGCsh6c22WGtjr+qGHj1otzZpV/1kwTMAqkwZsnWRU=
+github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 h1:pRhl55Yx1eC7BZ1N+BBWwnKaMyD8uC+34TLdndZMAKk=
+github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0/go.mod h1:XKMd7iuf/RGPSMJ/U4HP0zS2Z9Fh8Ps9a+6X26m/tmI=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.0 h1:VD1gqscl4nYs1YxVuSdemTrSgTKrwOWDK0FVFMqm+Cg=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.0/go.mod h1:4EgsQoS4TOhJizV+JTFg40qx1Ofh3XmXEQNBpgvNT40=
 github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
 github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
 github.com/jonboulle/clockwork v0.5.0 h1:Hyh9A8u51kptdkR+cqRpT1EebBwTn1oK9YfGYbdFz6I=
@@ -87,9 +89,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
 github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
 github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
 github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
-github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
-github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
-github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
+github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
+github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
 github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
 github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
 github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
@@ -108,8 +109,8 @@ github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/j
 github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
 github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
 github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
-github.com/prometheus/common v0.61.0 h1:3gv/GThfX0cV2lpO7gkTUwZru38mxevy90Bj8YFSRQQ=
-github.com/prometheus/common v0.61.0/go.mod h1:zr29OCN/2BsJRaFwG8QOBr41D6kkchKbpeNH7pAjb/s=
+github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
+github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
 github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
 github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
 github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
@@ -148,22 +149,22 @@ go.etcd.io/raft/v3 v3.6.0-beta.0 h1:MZFQVjCQxPJj5K9oS69Y+atNvYnGNyOQBnroTdw56jQ=
 go.etcd.io/raft/v3 v3.6.0-beta.0/go.mod h1:C2JoekRXfvImSrk5GnqD0aZ3a+cGVRnyem9qqn2DCEw=
 go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
 go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
-go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.58.0 h1:PS8wXpbyaDJQ2VDHHncMe9Vct0Zn1fEjpsjrLxGJoSc=
-go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.58.0/go.mod h1:HDBUsEjOuRC0EzKZ1bSaRGZWUBAzo+MhAcUUORSr4D0=
-go.opentelemetry.io/otel v1.33.0 h1:/FerN9bax5LoK51X/sI0SVYrjSE0/yUL7DpxW4K3FWw=
-go.opentelemetry.io/otel v1.33.0/go.mod h1:SUUkR6csvUQl+yjReHu5uM3EtVV7MBm5FHKRlNx4I8I=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0 h1:Vh5HayB/0HHfOQA7Ctx69E/Y/DcQSMPpKANYVMQ7fBA=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0/go.mod h1:cpgtDBaqD/6ok/UG0jT15/uKjAY8mRA53diogHBg3UI=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0 h1:5pojmb1U1AogINhN3SurB+zm/nIcusopeBNp42f45QM=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0/go.mod h1:57gTHJSE5S1tqg+EKsLPlTWhpHMsWlVmer+LA926XiA=
-go.opentelemetry.io/otel/metric v1.33.0 h1:r+JOocAyeRVXD8lZpjdQjzMadVZp2M4WmQ+5WtEnklQ=
-go.opentelemetry.io/otel/metric v1.33.0/go.mod h1:L9+Fyctbp6HFTddIxClbQkjtubW6O9QS3Ann/M82u6M=
-go.opentelemetry.io/otel/sdk v1.33.0 h1:iax7M131HuAm9QkZotNHEfstof92xM+N8sr3uHXc2IM=
-go.opentelemetry.io/otel/sdk v1.33.0/go.mod h1:A1Q5oi7/9XaMlIWzPSxLRWOI8nG3FnzHJNbiENQuihM=
-go.opentelemetry.io/otel/sdk/metric v1.31.0 h1:i9hxxLJF/9kkvfHppyLL55aW7iIJz4JjxTeYusH7zMc=
-go.opentelemetry.io/otel/sdk/metric v1.31.0/go.mod h1:CRInTMVvNhUKgSAMbKyTMxqOBC0zgyxzW55lZzX43Y8=
-go.opentelemetry.io/otel/trace v1.33.0 h1:cCJuF7LRjUFso9LPnEAHJDB2pqzp+hbO8eu1qqW2d/s=
-go.opentelemetry.io/otel/trace v1.33.0/go.mod h1:uIcdVUZMpTAmz0tI1z04GoVSezK37CbGV4fr1f2nBck=
+go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0 h1:rgMkmiGfix9vFJDcDi1PK8WEQP4FLQwLDfhp5ZLpFeE=
+go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0/go.mod h1:ijPqXp5P6IRRByFVVg9DY8P5HkxkHE5ARIa+86aXPf4=
+go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY=
+go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 h1:OeNbIYk/2C15ckl7glBlOBp5+WlYsOElzTNmiPW/x60=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0/go.mod h1:7Bept48yIeqxP2OZ9/AqIpYS94h2or0aB4FypJTc8ZM=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 h1:tgJ0uaNS4c98WRNUEx5U3aDlrDOI5Rs+1Vifcw4DJ8U=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0/go.mod h1:U7HYyW0zt/a9x5J1Kjs+r1f/d4ZHnYFclhYY2+YbeoE=
+go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ=
+go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE=
+go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A=
+go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU=
+go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU=
+go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ=
+go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k=
+go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE=
 go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4=
 go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4=
 go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
@@ -211,7 +212,6 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
 golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
@@ -238,19 +238,19 @@ google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
 google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
 google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
 google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
-google.golang.org/genproto/googleapis/api v0.0.0-20250102185135-69823020774d h1:H8tOf8XM88HvKqLTxe755haY6r1fqqzLbEnfrmLXlSA=
-google.golang.org/genproto/googleapis/api v0.0.0-20250102185135-69823020774d/go.mod h1:2v7Z7gP2ZUOGsaFyxATQSRoBnKygqVq2Cwnvom7QiqY=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d h1:xJJRGY7TJcvIlpSrN3K6LAWgNFUILlO+OMAqtg9aqnw=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d/go.mod h1:3ENsm/5D1mzDyhpzeRi1NR784I0BcofWBoSc5QqqMK4=
+google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f h1:gap6+3Gk41EItBuyi4XX/bp4oqJ3UwuIMl25yGinuAA=
+google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:Ic02D47M+zbarjYYUlK57y316f2MoN0gjAwI3f2S95o=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f h1:OxYkA3wjPsZyBylwymxSHa7ViiW1Sml4ToBrncvFehI=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:+2Yz8+CLJbIfL9z73EW45avw8Lmge3xVElCP9zEKi50=
 google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
 google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
 google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
 google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
-google.golang.org/grpc v1.69.4 h1:MF5TftSMkd8GLw/m0KM6V8CMOCY6NZ1NQDPGFgbTt4A=
-google.golang.org/grpc v1.69.4/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4=
-google.golang.org/protobuf v1.36.2 h1:R8FeyR1/eLmkutZOM5CWghmo5itiG9z0ktFlTVLuTmU=
-google.golang.org/protobuf v1.36.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
+google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ=
+google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw=
+google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM=
+google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
diff --git a/pkg/flags/selective_string.go b/pkg/flags/selective_string.go
index cfba14455..c0e4794a3 100644
--- a/pkg/flags/selective_string.go
+++ b/pkg/flags/selective_string.go
@@ -77,11 +77,10 @@ type SelectiveStringsValue struct {
 func (ss *SelectiveStringsValue) Set(s string) error {
 	vs := strings.Split(s, ",")
 	for i := range vs {
-		if _, ok := ss.valids[vs[i]]; ok {
-			ss.vs = append(ss.vs, vs[i])
-		} else {
+		if _, ok := ss.valids[vs[i]]; !ok {
 			return fmt.Errorf("invalid value %q", vs[i])
 		}
+		ss.vs = append(ss.vs, vs[i])
 	}
 	sort.Strings(ss.vs)
 	return nil
diff --git a/pkg/go.mod b/pkg/go.mod
index db9045725..317bb7749 100644
--- a/pkg/go.mod
+++ b/pkg/go.mod
@@ -12,7 +12,7 @@ require (
 	github.com/stretchr/testify v1.10.0
 	go.etcd.io/etcd/client/pkg/v3 v3.6.0-alpha.0
 	go.uber.org/zap v1.27.0
-	google.golang.org/grpc v1.69.4
+	google.golang.org/grpc v1.70.0
 )
 
 require (
@@ -20,14 +20,14 @@ require (
 	github.com/davecgh/go-spew v1.1.1 // indirect
 	github.com/inconshreveable/mousetrap v1.1.0 // indirect
 	github.com/pmezard/go-difflib v1.0.0 // indirect
-	go.opentelemetry.io/otel v1.33.0 // indirect
-	go.opentelemetry.io/otel/sdk v1.33.0 // indirect
+	go.opentelemetry.io/otel v1.34.0 // indirect
+	go.opentelemetry.io/otel/sdk v1.34.0 // indirect
 	go.uber.org/multierr v1.11.0 // indirect
 	golang.org/x/net v0.34.0 // indirect
 	golang.org/x/sys v0.29.0 // indirect
 	golang.org/x/text v0.21.0 // indirect
-	google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d // indirect
-	google.golang.org/protobuf v1.36.2 // indirect
+	google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect
+	google.golang.org/protobuf v1.36.4 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
 )
 
diff --git a/pkg/go.sum b/pkg/go.sum
index 72ceda400..67b921334 100644
--- a/pkg/go.sum
+++ b/pkg/go.sum
@@ -37,16 +37,16 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf
 github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
 go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
 go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
-go.opentelemetry.io/otel v1.33.0 h1:/FerN9bax5LoK51X/sI0SVYrjSE0/yUL7DpxW4K3FWw=
-go.opentelemetry.io/otel v1.33.0/go.mod h1:SUUkR6csvUQl+yjReHu5uM3EtVV7MBm5FHKRlNx4I8I=
-go.opentelemetry.io/otel/metric v1.33.0 h1:r+JOocAyeRVXD8lZpjdQjzMadVZp2M4WmQ+5WtEnklQ=
-go.opentelemetry.io/otel/metric v1.33.0/go.mod h1:L9+Fyctbp6HFTddIxClbQkjtubW6O9QS3Ann/M82u6M=
-go.opentelemetry.io/otel/sdk v1.33.0 h1:iax7M131HuAm9QkZotNHEfstof92xM+N8sr3uHXc2IM=
-go.opentelemetry.io/otel/sdk v1.33.0/go.mod h1:A1Q5oi7/9XaMlIWzPSxLRWOI8nG3FnzHJNbiENQuihM=
-go.opentelemetry.io/otel/sdk/metric v1.31.0 h1:i9hxxLJF/9kkvfHppyLL55aW7iIJz4JjxTeYusH7zMc=
-go.opentelemetry.io/otel/sdk/metric v1.31.0/go.mod h1:CRInTMVvNhUKgSAMbKyTMxqOBC0zgyxzW55lZzX43Y8=
-go.opentelemetry.io/otel/trace v1.33.0 h1:cCJuF7LRjUFso9LPnEAHJDB2pqzp+hbO8eu1qqW2d/s=
-go.opentelemetry.io/otel/trace v1.33.0/go.mod h1:uIcdVUZMpTAmz0tI1z04GoVSezK37CbGV4fr1f2nBck=
+go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY=
+go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI=
+go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ=
+go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE=
+go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A=
+go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU=
+go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU=
+go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ=
+go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k=
+go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE=
 go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
 go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
 go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
@@ -59,12 +59,12 @@ golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
 golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
 golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d h1:xJJRGY7TJcvIlpSrN3K6LAWgNFUILlO+OMAqtg9aqnw=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d/go.mod h1:3ENsm/5D1mzDyhpzeRi1NR784I0BcofWBoSc5QqqMK4=
-google.golang.org/grpc v1.69.4 h1:MF5TftSMkd8GLw/m0KM6V8CMOCY6NZ1NQDPGFgbTt4A=
-google.golang.org/grpc v1.69.4/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4=
-google.golang.org/protobuf v1.36.2 h1:R8FeyR1/eLmkutZOM5CWghmo5itiG9z0ktFlTVLuTmU=
-google.golang.org/protobuf v1.36.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f h1:OxYkA3wjPsZyBylwymxSHa7ViiW1Sml4ToBrncvFehI=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:+2Yz8+CLJbIfL9z73EW45avw8Lmge3xVElCP9zEKi50=
+google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ=
+google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw=
+google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM=
+google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
diff --git a/scripts/etcd_version_annotations.txt b/scripts/etcd_version_annotations.txt
index 3f0a0a676..10c02b888 100644
--- a/scripts/etcd_version_annotations.txt
+++ b/scripts/etcd_version_annotations.txt
@@ -160,6 +160,8 @@ etcdserverpb.DowngradeRequest.version: ""
 etcdserverpb.DowngradeResponse: "3.5"
 etcdserverpb.DowngradeResponse.header: ""
 etcdserverpb.DowngradeResponse.version: ""
+etcdserverpb.DowngradeVersionTestRequest: "3.6"
+etcdserverpb.DowngradeVersionTestRequest.ver: ""
 etcdserverpb.EmptyResponse: ""
 etcdserverpb.HashKVRequest: "3.3"
 etcdserverpb.HashKVRequest.revision: ""
@@ -201,6 +203,7 @@ etcdserverpb.InternalRaftRequest.cluster_version_set: "3.5"
 etcdserverpb.InternalRaftRequest.compaction: ""
 etcdserverpb.InternalRaftRequest.delete_range: ""
 etcdserverpb.InternalRaftRequest.downgrade_info_set: "3.5"
+etcdserverpb.InternalRaftRequest.downgrade_version_test: "3.6"
 etcdserverpb.InternalRaftRequest.header: ""
 etcdserverpb.InternalRaftRequest.lease_checkpoint: "3.4"
 etcdserverpb.InternalRaftRequest.lease_grant: ""
diff --git a/scripts/test_images.sh b/scripts/test_images.sh
index 3cdb606b2..f4f18a694 100755
--- a/scripts/test_images.sh
+++ b/scripts/test_images.sh
@@ -7,88 +7,132 @@ IFS=$'\n\t'
 source ./scripts/test_lib.sh
 source ./scripts/build_lib.sh
 
-function startContainer {
-    # run docker in the background
-    docker run -d --rm --name "${RUN_NAME}" "${IMAGE}"
-
-    # wait for etcd daemon to bootstrap
-    sleep 5
-}
-
-function runVersionCheck {
-    Out=$(docker run --rm "${IMAGE}" "${@}")
-    foundVersion=$(echo "$Out" | head -1 | rev  | cut -d" "  -f 1 | rev )
-    if [[ "${foundVersion}" != "${VERSION}" ]]; then
-        echo "error: Invalid Version. Got $foundVersion, expected $VERSION. Error: $Out"
-        exit 1
-    fi
-}
-
-# Can't proceed without docker
-if ! command -v docker >/dev/null; then
-    log_error "cannot find docker"
-    exit 1
-fi
-
-# You can't run darwin binaries in linux containers
+# Can't run darwin binaries in linux containers.
 if [[ $(go env GOOS) == "darwin" ]]; then
-    echo "Please use linux machine for release builds."
-    exit 1
+  log_error "Error: Please use a Linux machine to test release images builds."
+  exit 1
 fi
 
-# Pick defaults based on release workflow
-ARCH=$(go env GOARCH)
-REPOSITARY=${REPOSITARY:-"gcr.io/etcd-development/etcd"}
-if [ -n "$VERSION" ]; then
-    # Expected Format: v3.6.99-amd64
-    TAG=v"${VERSION}"-"${ARCH}"
-else
-    echo "Terminating test, VERSION not supplied"
-    exit 1
+# Can't proceed without Docker.
+if ! command -v docker >/dev/null; then
+  log_error "Error: Cannot find docker. Please follow the installation instructions at: https://docs.docker.com/get-docker/"
+  exit 1
 fi
-IMAGE=${IMAGE:-"${REPOSITARY}:${TAG}"}
 
-# ETCD related values
-RUN_NAME="test_etcd"
-KEY="foo"
-VALUE="bar"
+# Start a container with the given Docker image.
+function start_container {
+  local container_name=$1
+  local image=$2
 
-if [[ "$(docker images -q "${IMAGE}" 2> /dev/null)" == "" ]]; then
-    echo "${IMAGE} not present locally"
+    # run docker in the background
+    docker run --detach --rm --name "${container_name}" "${image}"
+
+    # wait for etcd daemon to bootstrap
+    local attempts=0
+    while ! docker exec "${container_name}" /usr/local/bin/etcdctl endpoint health --command-timeout=1s; do
+      sleep 1
+      attempts=$((attempts + 1))
+      if [ "${attempts}" -gt 10 ]; then
+        log_error "Error: etcd daemon failed to start."
+        exit 1
+      fi
+    done
+  }
+
+# Run a version check for the given Docker image.
+function run_version_check {
+  local output
+  local found_version
+  local image=$1
+  shift
+  local expected_version=$1
+  shift
+
+  output=$(docker run --rm "${image}" "${@}")
+  found_version=$(echo "${output}" | head -1 | rev | cut -d" " -f 1 | rev)
+  if [[ "${found_version}" != "${expected_version}" ]]; then
+    log_error "Error: Invalid Version."
+    log_error "Got ${found_version}, expected ${expected_version}."
+    log_error "Output: ${output}."
     exit 1
-fi
+  fi
+}
 
-# Version check
-runVersionCheck "/usr/local/bin/etcd" "--version"
-runVersionCheck "/usr/local/bin/etcdctl" "version"
-runVersionCheck "/usr/local/bin/etcdutl" "version"
+# Put a key-value pair in the etcd container and check if it can be retrieved,
+# and has the expected value.
+function put_get_check {
+  local container_name=$1
+  local key="foo"
+  local value="bar"
+  local result
+
+  result=$(docker exec "${container_name}" /usr/local/bin/etcdctl put "${key}" "${value}")
+  if [ "${result}" != "OK" ]; then
+    log_error "Error: Storing key failed. Result: ${result}."
+    exit 1
+  fi
 
-startContainer
-# stop container
-trap 'docker stop "${RUN_NAME}"' EXIT
+  result=$(docker exec "${container_name}" /usr/local/bin/etcdctl get "${key}" --print-value-only)
+  if [ "${result}" != "${value}" ]; then
+    log_error "Error: Problem with getting key. Got: ${result}, expected: ${value}."
+    exit 1
+  fi
+}
 
+# Verify that the images have the correct architecture.
+function verify_images_architecture {
+  local repository=$1
+  local version=$2
+  local target_arch
+  local arch_tag
+  local img_arch
+
+  for target_arch in "amd64" "arm64" "ppc64le" "s390x"; do
+    arch_tag="v${version}-${target_arch}"
+    img_arch=$(docker inspect --format '{{.Architecture}}' "${repository}:${arch_tag}")
+    if [ "${img_arch}" != "${target_arch}" ];then
+      log_error "Error: Incorrect Docker image architecture. Got ${img_arch}, expected: ${arch_tag}."
+      exit 1
+    fi
+    log_success "Correct architecture for ${arch_tag}."
+  done
+}
 
-# Put/Get check
-PUT=$(docker exec "${RUN_NAME}" /usr/local/bin/etcdctl put "${KEY}" "${VALUE}")
-if [ "${PUT}" != "OK" ]; then
-    echo "Problem with Putting in etcd"
+function main {
+  local version="$1"
+  local repository=${REPOSITORY:-"gcr.io/etcd-development/etcd"}
+  local arch
+  arch=$(go env GOARCH)
+  local tag="v${version}-${arch}"
+  local image="${TEST_IMAGE:-"${repository}:${tag}"}"
+  local container_name="test_etcd"
+
+  if [[ "$(docker images -q "${image}" 2> /dev/null)" == "" ]]; then
+    log_error "Error: ${image} not present locally."
     exit 1
-fi
+  fi
+
+  log_callout "Running version check."
+  run_version_check "${image}" "${version}" "/usr/local/bin/etcd" "--version"
+  run_version_check "${image}" "${version}" "/usr/local/bin/etcdctl" "version"
+  run_version_check "${image}" "${version}" "/usr/local/bin/etcdutl" "version"
+  log_success "Successfully ran version check."
+
+  log_callout "Running sanity check in Docker image."
+  start_container "${container_name}" "${image}"
+  # stop container
+  trap 'docker stop '"${container_name}" EXIT
+  put_get_check "${container_name}"
+  log_success "Successfully tested etcd local image ${tag}."
+
+  log_callout "Verifying images architecture."
+  verify_images_architecture "${repository}" "${version}"
+  log_success "Successfully tested images architecture."
+}
 
-GET=$(docker exec "${RUN_NAME}" /usr/local/bin/etcdctl get "$KEY" --print-value-only)
-if [ "${GET}" != "${VALUE}" ]; then
-    echo "Problem with getting foo bar in etcd. Got ${GET}"
-    exit 1
+if [ -z "$VERSION" ]; then
+  log_error "Error: VERSION not supplied."
+  exit 1
 fi
 
-echo "Successfully tested etcd local image ${TAG}"
-
-for TARGET_ARCH in "amd64" "arm64" "ppc64le" "s390x"; do
-    ARCH_TAG=v"${VERSION}"-"${TARGET_ARCH}"
-    IMG_ARCH=$(docker inspect --format '{{.Architecture}}' "${REPOSITARY}:${ARCH_TAG}")
-    if [ "${IMG_ARCH}" != "$TARGET_ARCH" ];then
-        echo "Incorrect docker image architecture"
-        exit 1
-    fi
-    echo "Correct Architecture ${ARCH_TAG}"
-done
+main "${VERSION}"
diff --git a/server/auth/jwt.go b/server/auth/jwt.go
index 4984dca62..e6aad1857 100644
--- a/server/auth/jwt.go
+++ b/server/auth/jwt.go
@@ -22,7 +22,7 @@ import (
 	"errors"
 	"time"
 
-	jwt "github.com/golang-jwt/jwt/v4"
+	"github.com/golang-jwt/jwt/v5"
 	"go.uber.org/zap"
 )
 
diff --git a/server/auth/metrics.go b/server/auth/metrics.go
index f7ce27920..8bf9470ae 100644
--- a/server/auth/metrics.go
+++ b/server/auth/metrics.go
@@ -21,12 +21,13 @@ import (
 )
 
 var (
-	currentAuthRevision = prometheus.NewGaugeFunc(prometheus.GaugeOpts{
-		Namespace: "etcd_debugging",
-		Subsystem: "auth",
-		Name:      "revision",
-		Help:      "The current revision of auth store.",
-	},
+	currentAuthRevision = prometheus.NewGaugeFunc(
+		prometheus.GaugeOpts{
+			Namespace: "etcd_debugging",
+			Subsystem: "auth",
+			Name:      "revision",
+			Help:      "The current revision of auth store.",
+		},
 		func() float64 {
 			reportCurrentAuthRevMu.RLock()
 			defer reportCurrentAuthRevMu.RUnlock()
diff --git a/server/auth/options.go b/server/auth/options.go
index 6d554fbc4..fa9db20c9 100644
--- a/server/auth/options.go
+++ b/server/auth/options.go
@@ -23,7 +23,7 @@ import (
 	"os"
 	"time"
 
-	jwt "github.com/golang-jwt/jwt/v4"
+	"github.com/golang-jwt/jwt/v5"
 )
 
 const (
diff --git a/server/config/config.go b/server/config/config.go
index dee41b86d..986bde50e 100644
--- a/server/config/config.go
+++ b/server/config/config.go
@@ -196,12 +196,12 @@ type ServerConfig struct {
 	// a shared buffer in its readonly check operations.
 	ExperimentalTxnModeWriteWithSharedBuffer bool `json:"experimental-txn-mode-write-with-shared-buffer"`
 
-	// ExperimentalBootstrapDefragThresholdMegabytes is the minimum number of megabytes needed to be freed for etcd server to
+	// BootstrapDefragThresholdMegabytes is the minimum number of megabytes needed to be freed for etcd server to
 	// consider running defrag during bootstrap. Needs to be set to non-zero value to take effect.
-	ExperimentalBootstrapDefragThresholdMegabytes uint `json:"experimental-bootstrap-defrag-threshold-megabytes"`
+	BootstrapDefragThresholdMegabytes uint `json:"bootstrap-defrag-threshold-megabytes"`
 
-	// ExperimentalMaxLearners sets a limit to the number of learner members that can exist in the cluster membership.
-	ExperimentalMaxLearners int `json:"experimental-max-learners"`
+	// MaxLearners sets a limit to the number of learner members that can exist in the cluster membership.
+	MaxLearners int `json:"max-learners"`
 
 	// V2Deprecation defines a phase of v2store deprecation process.
 	V2Deprecation V2DeprecationEnum `json:"v2-deprecation"`
@@ -211,6 +211,9 @@ type ServerConfig struct {
 
 	// ServerFeatureGate is a server level feature gate
 	ServerFeatureGate featuregate.FeatureGate
+
+	// Metrics types of metrics - should be either 'basic' or 'extensive'
+	Metrics string
 }
 
 // VerifyBootstrap sanity-checks the initial config for bootstrap case
diff --git a/server/embed/config.go b/server/embed/config.go
index a66adf97c..618457f85 100644
--- a/server/embed/config.go
+++ b/server/embed/config.go
@@ -133,9 +133,13 @@ var (
 	// This is the mapping from the non boolean `experimental-` to the new flags.
 	// TODO: delete in v3.7
 	experimentalNonBoolFlagMigrationMap = map[string]string{
-		"experimental-compact-hash-check-time": "compact-hash-check-time",
-		"experimental-corrupt-check-time":      "corrupt-check-time",
-		"experimental-compaction-batch-limit":  "compaction-batch-limit",
+		"experimental-compact-hash-check-time":              "compact-hash-check-time",
+		"experimental-corrupt-check-time":                   "corrupt-check-time",
+		"experimental-compaction-batch-limit":               "compaction-batch-limit",
+		"experimental-watch-progress-notify-interval":       "watch-progress-notify-interval",
+		"experimental-warning-apply-duration":               "warning-apply-duration",
+		"experimental-bootstrap-defrag-threshold-megabytes": "bootstrap-defrag-threshold-megabytes",
+		"experimental-max-learners":                         "max-learners",
 	}
 )
 
@@ -394,21 +398,34 @@ type Config struct {
 	ExperimentalCompactionBatchLimit int `json:"experimental-compaction-batch-limit"`
 	CompactionBatchLimit             int `json:"compaction-batch-limit"`
 	// ExperimentalCompactionSleepInterval is the sleep interval between every etcd compaction loop.
-	ExperimentalCompactionSleepInterval     time.Duration `json:"experimental-compaction-sleep-interval"`
+	ExperimentalCompactionSleepInterval time.Duration `json:"experimental-compaction-sleep-interval"`
+	// ExperimentalWatchProgressNotifyInterval is the time duration of periodic watch progress notifications.
+	// Deprecated in v3.6 and will be decommissioned in v3.7.
+	// TODO: Delete in v3.7
 	ExperimentalWatchProgressNotifyInterval time.Duration `json:"experimental-watch-progress-notify-interval"`
+	WatchProgressNotifyInterval             time.Duration `json:"watch-progress-notify-interval"`
 	// ExperimentalWarningApplyDuration is the time duration after which a warning is generated if applying request
 	// takes more time than this value.
+	// Deprecated in v3.6 and will be decommissioned in v3.7.
+	// TODO: Delete in v3.7
 	ExperimentalWarningApplyDuration time.Duration `json:"experimental-warning-apply-duration"`
+	WarningApplyDuration             time.Duration `json:"warning-apply-duration"`
 	// ExperimentalBootstrapDefragThresholdMegabytes is the minimum number of megabytes needed to be freed for etcd server to
 	// consider running defrag during bootstrap. Needs to be set to non-zero value to take effect.
+	// Deprecated in v3.6 and will be decommissioned in v3.7.
+	// TODO: Delete in v3.7
 	ExperimentalBootstrapDefragThresholdMegabytes uint `json:"experimental-bootstrap-defrag-threshold-megabytes"`
+	BootstrapDefragThresholdMegabytes             uint `json:"bootstrap-defrag-threshold-megabytes"`
 	// WarningUnaryRequestDuration is the time duration after which a warning is generated if applying
 	// unary request takes more time than this value.
 	WarningUnaryRequestDuration time.Duration `json:"warning-unary-request-duration"`
 	// ExperimentalWarningUnaryRequestDuration is deprecated, please use WarningUnaryRequestDuration instead.
 	ExperimentalWarningUnaryRequestDuration time.Duration `json:"experimental-warning-unary-request-duration"`
 	// ExperimentalMaxLearners sets a limit to the number of learner members that can exist in the cluster membership.
+	// Deprecated in v3.6 and will be decommissioned in v3.7.
+	// TODO: Delete in v3.7
 	ExperimentalMaxLearners int `json:"experimental-max-learners"`
+	MaxLearners             int `json:"max-learners"`
 
 	// ForceNewCluster starts a new cluster even if previously started; unsafe.
 	ForceNewCluster bool `json:"force-new-cluster"`
@@ -546,10 +563,10 @@ func NewConfig() *Config {
 		SnapshotCount:          etcdserver.DefaultSnapshotCount,
 		SnapshotCatchUpEntries: etcdserver.DefaultSnapshotCatchUpEntries,
 
-		MaxTxnOps:                        DefaultMaxTxnOps,
-		MaxRequestBytes:                  DefaultMaxRequestBytes,
-		MaxConcurrentStreams:             DefaultMaxConcurrentStreams,
-		ExperimentalWarningApplyDuration: DefaultWarningApplyDuration,
+		MaxTxnOps:            DefaultMaxTxnOps,
+		MaxRequestBytes:      DefaultMaxRequestBytes,
+		MaxConcurrentStreams: DefaultMaxConcurrentStreams,
+		WarningApplyDuration: DefaultWarningApplyDuration,
 
 		GRPCKeepAliveMinTime:  DefaultGRPCKeepAliveMinTime,
 		GRPCKeepAliveInterval: DefaultGRPCKeepAliveInterval,
@@ -596,7 +613,9 @@ func NewConfig() *Config {
 		ExperimentalDowngradeCheckTime:      DefaultDowngradeCheckTime,
 		ExperimentalMemoryMlock:             false,
 		ExperimentalStopGRPCServiceOnDefrag: false,
-		ExperimentalMaxLearners:             membership.DefaultMaxLearners,
+		MaxLearners:                         membership.DefaultMaxLearners,
+		// TODO: delete in v3.7
+		ExperimentalMaxLearners: membership.DefaultMaxLearners,
 
 		CompactHashCheckTime: DefaultCompactHashCheckTime,
 		// TODO: delete in v3.7
@@ -797,16 +816,24 @@ func (cfg *Config) AddFlags(fs *flag.FlagSet) {
 	fs.IntVar(&cfg.ExperimentalCompactionBatchLimit, "experimental-compaction-batch-limit", cfg.ExperimentalCompactionBatchLimit, "Sets the maximum revisions deleted in each compaction batch. Deprecated in v3.6 and will be decommissioned in v3.7. Use --compaction-batch-limit instead.")
 	fs.IntVar(&cfg.CompactionBatchLimit, "compaction-batch-limit", cfg.CompactionBatchLimit, "Sets the maximum revisions deleted in each compaction batch.")
 	fs.DurationVar(&cfg.ExperimentalCompactionSleepInterval, "experimental-compaction-sleep-interval", cfg.ExperimentalCompactionSleepInterval, "Sets the sleep interval between each compaction batch.")
-	fs.DurationVar(&cfg.ExperimentalWatchProgressNotifyInterval, "experimental-watch-progress-notify-interval", cfg.ExperimentalWatchProgressNotifyInterval, "Duration of periodic watch progress notifications.")
+	// TODO: delete in v3.7
+	fs.DurationVar(&cfg.ExperimentalWatchProgressNotifyInterval, "experimental-watch-progress-notify-interval", cfg.ExperimentalWatchProgressNotifyInterval, "Duration of periodic watch progress notifications. Deprecated in v3.6 and will be decommissioned in v3.7. Use --watch-progress-notify-interval instead.")
+	fs.DurationVar(&cfg.WatchProgressNotifyInterval, "watch-progress-notify-interval", cfg.WatchProgressNotifyInterval, "Duration of periodic watch progress notifications.")
 	fs.DurationVar(&cfg.ExperimentalDowngradeCheckTime, "experimental-downgrade-check-time", cfg.ExperimentalDowngradeCheckTime, "Duration of time between two downgrade status checks.")
-	fs.DurationVar(&cfg.ExperimentalWarningApplyDuration, "experimental-warning-apply-duration", cfg.ExperimentalWarningApplyDuration, "Time duration after which a warning is generated if request takes more time.")
+	// TODO: delete in v3.7
+	fs.DurationVar(&cfg.ExperimentalWarningApplyDuration, "experimental-warning-apply-duration", cfg.ExperimentalWarningApplyDuration, "Time duration after which a warning is generated if request takes more time. Deprecated in v3.6 and will be decommissioned in v3.7. Use --warning-watch-progress-duration instead.")
+	fs.DurationVar(&cfg.WarningApplyDuration, "warning-apply-duration", cfg.WarningApplyDuration, "Time duration after which a warning is generated if watch progress takes more time.")
 	fs.DurationVar(&cfg.WarningUnaryRequestDuration, "warning-unary-request-duration", cfg.WarningUnaryRequestDuration, "Time duration after which a warning is generated if a unary request takes more time.")
 	fs.DurationVar(&cfg.ExperimentalWarningUnaryRequestDuration, "experimental-warning-unary-request-duration", cfg.ExperimentalWarningUnaryRequestDuration, "Time duration after which a warning is generated if a unary request takes more time. It's deprecated, and will be decommissioned in v3.7. Use --warning-unary-request-duration instead.")
 	fs.BoolVar(&cfg.ExperimentalMemoryMlock, "experimental-memory-mlock", cfg.ExperimentalMemoryMlock, "Enable to enforce etcd pages (in particular bbolt) to stay in RAM.")
 	fs.BoolVar(&cfg.ExperimentalTxnModeWriteWithSharedBuffer, "experimental-txn-mode-write-with-shared-buffer", true, "Enable the write transaction to use a shared buffer in its readonly check operations.")
 	fs.BoolVar(&cfg.ExperimentalStopGRPCServiceOnDefrag, "experimental-stop-grpc-service-on-defrag", cfg.ExperimentalStopGRPCServiceOnDefrag, "Enable etcd gRPC service to stop serving client requests on defragmentation.")
-	fs.UintVar(&cfg.ExperimentalBootstrapDefragThresholdMegabytes, "experimental-bootstrap-defrag-threshold-megabytes", 0, "Enable the defrag during etcd server bootstrap on condition that it will free at least the provided threshold of disk space. Needs to be set to non-zero value to take effect.")
-	fs.IntVar(&cfg.ExperimentalMaxLearners, "experimental-max-learners", membership.DefaultMaxLearners, "Sets the maximum number of learners that can be available in the cluster membership.")
+	// TODO: delete in v3.7
+	fs.UintVar(&cfg.ExperimentalBootstrapDefragThresholdMegabytes, "experimental-bootstrap-defrag-threshold-megabytes", 0, "Enable the defrag during etcd server bootstrap on condition that it will free at least the provided threshold of disk space. Needs to be set to non-zero value to take effect. It's deprecated, and will be decommissioned in v3.7. Use --bootstrap-defrag-threshold-megabytes instead.")
+	fs.UintVar(&cfg.BootstrapDefragThresholdMegabytes, "bootstrap-defrag-threshold-megabytes", 0, "Enable the defrag during etcd server bootstrap on condition that it will free at least the provided threshold of disk space. Needs to be set to non-zero value to take effect.")
+	// TODO: delete in v3.7
+	fs.IntVar(&cfg.ExperimentalMaxLearners, "experimental-max-learners", membership.DefaultMaxLearners, "Sets the maximum number of learners that can be available in the cluster membership. Deprecated in v3.6 and will be decommissioned in v3.7. Use --max-learners instead.")
+	fs.IntVar(&cfg.MaxLearners, "max-learners", membership.DefaultMaxLearners, "Sets the maximum number of learners that can be available in the cluster membership.")
 	fs.Uint64Var(&cfg.SnapshotCatchUpEntries, "experimental-snapshot-catchup-entries", cfg.SnapshotCatchUpEntries, "Number of entries for a slow follower to catch up after compacting the raft storage entries.")
 
 	// unsafe
diff --git a/server/embed/etcd.go b/server/embed/etcd.go
index f5fa2c2c6..5b3c3677b 100644
--- a/server/embed/etcd.go
+++ b/server/embed/etcd.go
@@ -31,7 +31,6 @@ import (
 	"sync"
 	"time"
 
-	grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus"
 	"github.com/soheilhy/cmux"
 	"go.uber.org/zap"
 	"google.golang.org/grpc"
@@ -79,12 +78,24 @@ type Etcd struct {
 
 	Server *etcdserver.EtcdServer
 
-	cfg   Config
-	stopc chan struct{}
-	errc  chan error
+	cfg Config
 
+	// closeOnce is to ensure `stopc` is closed only once, no matter
+	// how many times the Close() method is called.
 	closeOnce sync.Once
-	wg        sync.WaitGroup
+	// stopc is used to notify the sub goroutines not to send
+	// any errors to `errc`.
+	stopc chan struct{}
+	// errc is used to receive error from sub goroutines (including
+	// client handler, peer handler and metrics handler). It's closed
+	// after all these sub goroutines exit (checked via `wg`). Writers
+	// should avoid writing after `stopc` is closed by selecting on
+	// reading from `stopc`.
+	errc chan error
+
+	// wg is used to track the lifecycle of all sub goroutines which
+	// need to send error back to the `errc`.
+	wg sync.WaitGroup
 }
 
 type peerListener struct {
@@ -217,16 +228,17 @@ func StartEtcd(inCfg *Config) (e *Etcd, err error) {
 		LeaseCheckpointPersist:               cfg.ExperimentalEnableLeaseCheckpointPersist,
 		CompactionBatchLimit:                 cfg.CompactionBatchLimit,
 		CompactionSleepInterval:              cfg.ExperimentalCompactionSleepInterval,
-		WatchProgressNotifyInterval:          cfg.ExperimentalWatchProgressNotifyInterval,
+		WatchProgressNotifyInterval:          cfg.WatchProgressNotifyInterval,
 		DowngradeCheckTime:                   cfg.ExperimentalDowngradeCheckTime,
-		WarningApplyDuration:                 cfg.ExperimentalWarningApplyDuration,
+		WarningApplyDuration:                 cfg.WarningApplyDuration,
 		WarningUnaryRequestDuration:          cfg.WarningUnaryRequestDuration,
 		ExperimentalMemoryMlock:              cfg.ExperimentalMemoryMlock,
-		ExperimentalBootstrapDefragThresholdMegabytes: cfg.ExperimentalBootstrapDefragThresholdMegabytes,
-		ExperimentalMaxLearners:                       cfg.ExperimentalMaxLearners,
-		V2Deprecation:                                 cfg.V2DeprecationEffective(),
-		ExperimentalLocalAddress:                      cfg.InferLocalAddr(),
-		ServerFeatureGate:                             cfg.ServerFeatureGate,
+		BootstrapDefragThresholdMegabytes:    cfg.BootstrapDefragThresholdMegabytes,
+		MaxLearners:                          cfg.MaxLearners,
+		V2Deprecation:                        cfg.V2DeprecationEffective(),
+		ExperimentalLocalAddress:             cfg.InferLocalAddr(),
+		ServerFeatureGate:                    cfg.ServerFeatureGate,
+		Metrics:                              cfg.Metrics,
 	}
 
 	if srvcfg.ExperimentalEnableDistributedTracing {
@@ -374,7 +386,7 @@ func print(lg *zap.Logger, ec Config, sc config.ServerConfig, memberInitialized
 		zap.String("discovery-user", sc.DiscoveryCfg.Auth.Username),
 
 		zap.String("downgrade-check-interval", sc.DowngradeCheckTime.String()),
-		zap.Int("max-learners", sc.ExperimentalMaxLearners),
+		zap.Int("max-learners", sc.MaxLearners),
 
 		zap.String("v2-deprecation", string(ec.V2Deprecation)),
 	)
@@ -388,6 +400,24 @@ func (e *Etcd) Config() Config {
 // Close gracefully shuts down all servers/listeners.
 // Client requests will be terminated with request timeout.
 // After timeout, enforce remaning requests be closed immediately.
+//
+// The rough workflow to shut down etcd:
+//  1. close the `stopc` channel, so that all error handlers (child
+//     goroutines) won't send back any errors anymore;
+//  2. stop the http and grpc servers gracefully, within request timeout;
+//  3. close all client and metrics listeners, so that etcd server
+//     stops receiving any new connection;
+//  4. call the cancel function to close the gateway context, so that
+//     all gateway connections are closed.
+//  5. stop etcd server gracefully, and ensure the main raft loop
+//     goroutine is stopped;
+//  6. stop all peer listeners, so that it stops receiving peer connections
+//     and messages (wait up to 1-second);
+//  7. wait for all child goroutines (i.e. client handlers, peer handlers
+//     and metrics handlers) to exit;
+//  8. close the `errc` channel to release the resource. Note that it's only
+//     safe to close the `errc` after step 7 above is done, otherwise the
+//     child goroutines may send errors back to already closed `errc` channel.
 func (e *Etcd) Close() {
 	fields := []zap.Field{
 		zap.String("name", e.cfg.Name),
@@ -607,14 +637,15 @@ func (e *Etcd) servePeers() {
 
 	// start peer servers in a goroutine
 	for _, pl := range e.Peers {
-		go func(l *peerListener) {
+		l := pl
+		e.startHandler(func() error {
 			u := l.Addr().String()
 			e.cfg.logger.Info(
 				"serving peer traffic",
 				zap.String("address", u),
 			)
-			e.errHandler(l.serve())
-		}(pl)
+			return l.serve()
+		})
 	}
 }
 
@@ -774,9 +805,10 @@ func (e *Etcd) serveClients() {
 
 	// start client servers in each goroutine
 	for _, sctx := range e.sctxs {
-		go func(s *serveCtx) {
-			e.errHandler(s.serve(e.Server, &e.cfg.ClientTLSInfo, mux, e.errHandler, e.grpcGatewayDial(splitHTTP), splitHTTP, gopts...))
-		}(sctx)
+		s := sctx
+		e.startHandler(func() error {
+			return s.serve(e.Server, &e.cfg.ClientTLSInfo, mux, e.errHandler, e.grpcGatewayDial(splitHTTP), splitHTTP, gopts...)
+		})
 	}
 }
 
@@ -844,37 +876,41 @@ func (e *Etcd) createMetricsListener(murl url.URL) (net.Listener, error) {
 }
 
 func (e *Etcd) serveMetrics() (err error) {
-	if e.cfg.Metrics == "extensive" {
-		grpc_prometheus.EnableHandlingTimeHistogram()
-	}
-
 	if len(e.cfg.ListenMetricsUrls) > 0 {
 		metricsMux := http.NewServeMux()
 		etcdhttp.HandleMetrics(metricsMux)
 		etcdhttp.HandleHealth(e.cfg.logger, metricsMux, e.Server)
 
 		for _, murl := range e.cfg.ListenMetricsUrls {
+			u := murl
 			ml, err := e.createMetricsListener(murl)
 			if err != nil {
 				return err
 			}
 			e.metricsListeners = append(e.metricsListeners, ml)
-			go func(u url.URL, ln net.Listener) {
+
+			e.startHandler(func() error {
 				e.cfg.logger.Info(
 					"serving metrics",
 					zap.String("address", u.String()),
 				)
-				e.errHandler(http.Serve(ln, metricsMux))
-			}(murl, ml)
+				return http.Serve(ml, metricsMux)
+			})
 		}
 	}
 	return nil
 }
 
-func (e *Etcd) errHandler(err error) {
+func (e *Etcd) startHandler(handler func() error) {
+	// start each handler in a separate goroutine
 	e.wg.Add(1)
-	defer e.wg.Done()
+	go func() {
+		defer e.wg.Done()
+		e.errHandler(handler())
+	}()
+}
 
+func (e *Etcd) errHandler(err error) {
 	if err != nil {
 		e.GetLogger().Error("setting up serving from embedded etcd failed.", zap.Error(err))
 	}
diff --git a/server/embed/serve.go b/server/embed/serve.go
index c888e3b1c..e50529dfe 100644
--- a/server/embed/serve.go
+++ b/server/embed/serve.go
@@ -61,13 +61,35 @@ type serveCtx struct {
 	insecure bool
 	httpOnly bool
 
+	// ctx is used to control the grpc gateway. Terminate the grpc gateway
+	// by calling `cancel` when shutting down the etcd.
 	ctx    context.Context
 	cancel context.CancelFunc
 
 	userHandlers    map[string]http.Handler
 	serviceRegister func(*grpc.Server)
-	serversC        chan *servers
-	closeOnce       sync.Once
+
+	// serversC is used to receive the http and grpc server objects (created
+	// in `serve`), both of which will be closed when shutting down the etcd.
+	// Close it when `serve` returns or when etcd fails to bootstrap.
+	serversC chan *servers
+	// closeOnce is to ensure `serversC` is closed only once.
+	closeOnce sync.Once
+
+	// wg is used to track the lifecycle of all sub goroutines created by `serve`.
+	wg sync.WaitGroup
+}
+
+func (sctx *serveCtx) startHandler(errHandler func(error), handler func() error) {
+	// start each handler in a separate goroutine
+	sctx.wg.Add(1)
+	go func() {
+		defer sctx.wg.Done()
+		err := handler()
+		if errHandler != nil {
+			errHandler(err)
+		}
+	}()
 }
 
 type servers struct {
@@ -182,15 +204,15 @@ func (sctx *serveCtx) serve(
 			server = m.Serve
 
 			httpl := m.Match(cmux.HTTP1())
-			go func(srvhttp *http.Server, tlsLis net.Listener) {
-				errHandler(srvhttp.Serve(tlsLis))
-			}(srv, httpl)
+			sctx.startHandler(errHandler, func() error {
+				return srv.Serve(httpl)
+			})
 
 			if grpcEnabled {
 				grpcl := m.Match(cmux.HTTP2())
-				go func(gs *grpc.Server, l net.Listener) {
-					errHandler(gs.Serve(l))
-				}(gs, grpcl)
+				sctx.startHandler(errHandler, func() error {
+					return gs.Serve(grpcl)
+				})
 			}
 		}
 
@@ -237,7 +259,7 @@ func (sctx *serveCtx) serve(
 				TLSConfig: tlscfg,
 				ErrorLog:  logger, // do not log user error
 			}
-			if err := configureHTTPServer(srv, s.Cfg); err != nil {
+			if err = configureHTTPServer(srv, s.Cfg); err != nil {
 				sctx.lg.Error("Configure https server failed", zap.Error(err))
 				return err
 			}
@@ -248,13 +270,13 @@ func (sctx *serveCtx) serve(
 		} else {
 			server = m.Serve
 
-			tlsl, err := transport.NewTLSListener(m.Match(cmux.Any()), tlsinfo)
-			if err != nil {
-				return err
+			tlsl, tlsErr := transport.NewTLSListener(m.Match(cmux.Any()), tlsinfo)
+			if tlsErr != nil {
+				return tlsErr
 			}
-			go func(srvhttp *http.Server, tlsl net.Listener) {
-				errHandler(srvhttp.Serve(tlsl))
-			}(srv, tlsl)
+			sctx.startHandler(errHandler, func() error {
+				return srv.Serve(tlsl)
+			})
 		}
 
 		sctx.serversC <- &servers{secure: true, grpc: gs, http: srv}
@@ -265,7 +287,10 @@ func (sctx *serveCtx) serve(
 		)
 	}
 
-	return server()
+	err = server()
+	sctx.close()
+	sctx.wg.Wait()
+	return err
 }
 
 func configureHTTPServer(srv *http.Server, cfg config.ServerConfig) error {
@@ -334,7 +359,7 @@ func (sctx *serveCtx) registerGateway(dial func(ctx context.Context) (*grpc.Clie
 			return nil, err
 		}
 	}
-	go func() {
+	sctx.startHandler(nil, func() error {
 		<-ctx.Done()
 		if cerr := conn.Close(); cerr != nil {
 			sctx.lg.Warn(
@@ -343,7 +368,8 @@ func (sctx *serveCtx) registerGateway(dial func(ctx context.Context) (*grpc.Clie
 				zap.Error(cerr),
 			)
 		}
-	}()
+		return nil
+	})
 
 	return gwmux, nil
 }
diff --git a/server/etcdmain/config.go b/server/etcdmain/config.go
index 99cc279a1..907bf6bfe 100644
--- a/server/etcdmain/config.go
+++ b/server/etcdmain/config.go
@@ -62,11 +62,15 @@ var (
 		"snapshot-count": "--snapshot-count is deprecated in 3.6 and will be decommissioned in 3.7.",
 		"max-snapshots":  "--max-snapshots is deprecated in 3.6 and will be decommissioned in 3.7.",
 		"v2-deprecation": "--v2-deprecation is deprecated and scheduled for removal in v3.8. The default value is enforced, ignoring user input.",
-		"experimental-compact-hash-check-enabled":        "--experimental-compact-hash-check-enabled is deprecated in 3.6 and will be decommissioned in 3.7. Use '--feature-gates=CompactHashCheck=true' instead.",
-		"experimental-compact-hash-check-time":           "--experimental-compact-hash-check-time is deprecated in 3.6 and will be decommissioned in 3.7. Use '--compact-hash-check-time' instead.",
-		"experimental-txn-mode-write-with-shared-buffer": "--experimental-txn-mode-write-with-shared-buffer is deprecated in v3.6 and will be decommissioned in v3.7. Use '--feature-gates=TxnModeWriteWithSharedBuffer=true' instead.",
-		"experimental-corrupt-check-time":                "--experimental-corrupt-check-time is deprecated in v3.6 and will be decommissioned in v3.7. Use '--corrupt-check-time' instead.",
-		"experimental-compaction-batch-limit":            "--experimental-compaction-batch-limit is deprecated in v3.6 and will be decommissioned in v3.7. Use '--compaction-batch-limit' instead.",
+		"experimental-compact-hash-check-enabled":           "--experimental-compact-hash-check-enabled is deprecated in 3.6 and will be decommissioned in 3.7. Use '--feature-gates=CompactHashCheck=true' instead.",
+		"experimental-compact-hash-check-time":              "--experimental-compact-hash-check-time is deprecated in 3.6 and will be decommissioned in 3.7. Use '--compact-hash-check-time' instead.",
+		"experimental-txn-mode-write-with-shared-buffer":    "--experimental-txn-mode-write-with-shared-buffer is deprecated in v3.6 and will be decommissioned in v3.7. Use '--feature-gates=TxnModeWriteWithSharedBuffer=true' instead.",
+		"experimental-corrupt-check-time":                   "--experimental-corrupt-check-time is deprecated in v3.6 and will be decommissioned in v3.7. Use '--corrupt-check-time' instead.",
+		"experimental-compaction-batch-limit":               "--experimental-compaction-batch-limit is deprecated in v3.6 and will be decommissioned in v3.7. Use '--compaction-batch-limit' instead.",
+		"experimental-watch-progress-notify-interval":       "--experimental-watch-progress-notify-interval is deprecated in v3.6 and will be decommissioned in v3.7. Use '--watch-progress-notify-interval' instead.",
+		"experimental-warning-apply-duration":               "--experimental-warning-apply-duration is deprecated in v3.6 and will be decommissioned in v3.7. Use '--warning-apply-duration' instead.",
+		"experimental-bootstrap-defrag-threshold-megabytes": "--experimental-bootstrap-defrag-threshold-megabytes is deprecated in v3.6 and will be decommissioned in v3.7. Use '--bootstrap-defrag-threshold-megabytes' instead.",
+		"experimental-max-learners":                         "--experimental-max-learners is deprecated in v3.6 and will be decommissioned in v3.7. Use '--max-learners' instead.",
 	}
 )
 
@@ -184,6 +188,22 @@ func (cfg *config) parse(arguments []string) error {
 		cfg.ec.CompactionBatchLimit = cfg.ec.ExperimentalCompactionBatchLimit
 	}
 
+	if cfg.ec.FlagsExplicitlySet["experimental-watch-progress-notify-interval"] {
+		cfg.ec.WatchProgressNotifyInterval = cfg.ec.ExperimentalWatchProgressNotifyInterval
+	}
+
+	if cfg.ec.FlagsExplicitlySet["experimental-warning-apply-duration"] {
+		cfg.ec.WarningApplyDuration = cfg.ec.ExperimentalWarningApplyDuration
+	}
+
+	if cfg.ec.FlagsExplicitlySet["experimental-bootstrap-defrag-threshold-megabytes"] {
+		cfg.ec.BootstrapDefragThresholdMegabytes = cfg.ec.ExperimentalBootstrapDefragThresholdMegabytes
+	}
+
+	if cfg.ec.FlagsExplicitlySet["experimental-max-learners"] {
+		cfg.ec.MaxLearners = cfg.ec.ExperimentalMaxLearners
+	}
+
 	// `V2Deprecation` (--v2-deprecation) is deprecated and scheduled for removal in v3.8. The default value is enforced, ignoring user input.
 	cfg.ec.V2Deprecation = cconfig.V2DeprDefault
 
diff --git a/server/etcdmain/grpc_proxy.go b/server/etcdmain/grpc_proxy.go
index a0df3f99a..9aeae162c 100644
--- a/server/etcdmain/grpc_proxy.go
+++ b/server/etcdmain/grpc_proxy.go
@@ -30,8 +30,9 @@ import (
 	"time"
 
 	grpc_zap "github.com/grpc-ecosystem/go-grpc-middleware/logging/zap"
+	grpc_prometheus "github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus"
 	grpc_ctxtags "github.com/grpc-ecosystem/go-grpc-middleware/tags"
-	grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus"
+	"github.com/prometheus/client_golang/prometheus"
 	"github.com/soheilhy/cmux"
 	"github.com/spf13/cobra"
 	"go.uber.org/zap"
@@ -505,11 +506,14 @@ func newGRPCProxyServer(lg *zap.Logger, client *clientv3.Client) *grpc.Server {
 
 	alwaysLoggingDeciderServer := func(ctx context.Context, fullMethodName string, servingObject any) bool { return true }
 
+	serverMetrics := grpc_prometheus.NewServerMetrics()
+	prometheus.MustRegister(serverMetrics)
+
 	grpcChainStreamList := []grpc.StreamServerInterceptor{
-		grpc_prometheus.StreamServerInterceptor,
+		serverMetrics.StreamServerInterceptor(),
 	}
 	grpcChainUnaryList := []grpc.UnaryServerInterceptor{
-		grpc_prometheus.UnaryServerInterceptor,
+		serverMetrics.UnaryServerInterceptor(),
 	}
 	if grpcProxyEnableLogging {
 		grpcChainStreamList = append(grpcChainStreamList,
diff --git a/server/etcdmain/help.go b/server/etcdmain/help.go
index 832716fb2..986c0d519 100644
--- a/server/etcdmain/help.go
+++ b/server/etcdmain/help.go
@@ -294,16 +294,24 @@ Experimental feature:
   --experimental-peer-skip-client-san-verification 'false'
     Skip verification of SAN field in client certificate for peer connections.
   --experimental-watch-progress-notify-interval '10m'
+    Duration of periodical watch progress notification. Deprecated in v3.6 and will be decommissioned in v3.7. Use 'watch-progress-notify-interval' instead.
+  --watch-progress-notify-interval '10m'
     Duration of periodical watch progress notification.
   --experimental-warning-apply-duration '100ms'
+    Warning is generated if requests take more than this duration. Deprecated in v3.6 and will be decommissioned in v3.7. Use 'warning-apply-duration' instead.
+  --warning-apply-duration '100ms'
     Warning is generated if requests take more than this duration.
   --experimental-txn-mode-write-with-shared-buffer 'true'. Deprecated in v3.6 and will be decommissioned in v3.7. Use '--feature-gates=TxnModeWriteWithSharedBuffer=true' instead.
     Enable the write transaction to use a shared buffer in its readonly check operations.
   --experimental-bootstrap-defrag-threshold-megabytes
+    Enable the defrag during etcd server bootstrap on condition that it will free at least the provided threshold of disk space. Needs to be set to non-zero value to take effect. Deprecated in v3.6 and will be decommissioned in v3.7. Use 'bootstrap-defrag-threshold-megabytes' instead.
+  --bootstrap-defrag-threshold-megabytes
     Enable the defrag during etcd server bootstrap on condition that it will free at least the provided threshold of disk space. Needs to be set to non-zero value to take effect.
   --experimental-warning-unary-request-duration '300ms'
     Set time duration after which a warning is generated if a unary request takes more than this duration. It's deprecated, and will be decommissioned in v3.7. Use --warning-unary-request-duration instead.
   --experimental-max-learners '1'
+    Set the max number of learner members allowed in the cluster membership. Deprecated in v3.6 and will be decommissioned in v3.7. Use 'max-learners' instead.
+  --max-learners '1'
     Set the max number of learner members allowed in the cluster membership.
   --experimental-snapshot-catch-up-entries '5000'
     Number of entries for a slow follower to catch up after compacting the raft storage entries.
diff --git a/server/etcdserver/api/etcdhttp/health.go b/server/etcdserver/api/etcdhttp/health.go
index ccc6d5b72..26ed4cab1 100644
--- a/server/etcdserver/api/etcdhttp/health.go
+++ b/server/etcdserver/api/etcdhttp/health.go
@@ -52,6 +52,7 @@ type ServerHealth interface {
 	Range(context.Context, *pb.RangeRequest) (*pb.RangeResponse, error)
 	Config() config.ServerConfig
 	AuthStore() auth.AuthStore
+	IsLearner() bool
 }
 
 // HandleHealth registers metrics and health handlers. it checks health by using v3 range request
@@ -119,20 +120,22 @@ var (
 		Name:      "health_failures",
 		Help:      "The total number of failed health checks",
 	})
-	healthCheckGauge = prometheus.NewGaugeVec(prometheus.GaugeOpts{
-		Namespace: "etcd",
-		Subsystem: "server",
-		Name:      "healthcheck",
-		Help:      "The result of each kind of healthcheck.",
-	},
+	healthCheckGauge = prometheus.NewGaugeVec(
+		prometheus.GaugeOpts{
+			Namespace: "etcd",
+			Subsystem: "server",
+			Name:      "healthcheck",
+			Help:      "The result of each kind of healthcheck.",
+		},
 		[]string{"type", "name"},
 	)
-	healthCheckCounter = prometheus.NewCounterVec(prometheus.CounterOpts{
-		Namespace: "etcd",
-		Subsystem: "server",
-		Name:      "healthchecks_total",
-		Help:      "The total number of each kind of healthcheck.",
-	},
+	healthCheckCounter = prometheus.NewCounterVec(
+		prometheus.CounterOpts{
+			Namespace: "etcd",
+			Subsystem: "server",
+			Name:      "healthchecks_total",
+			Help:      "The total number of each kind of healthcheck.",
+		},
 		[]string{"type", "name", "status"},
 	)
 )
@@ -252,6 +255,8 @@ func installReadyzEndpoints(lg *zap.Logger, mux *http.ServeMux, server ServerHea
 	reg.Register("serializable_read", readCheck(server, true))
 	// linearizable_read check would be replaced by read_index check in 3.6
 	reg.Register("linearizable_read", readCheck(server, false))
+	// check if local is learner
+	reg.Register("non_learner", learnerCheck(server))
 	reg.InstallHTTPEndpoints(lg, mux)
 }
 
@@ -431,3 +436,12 @@ func readCheck(srv ServerHealth, serializable bool) func(ctx context.Context) er
 		return err
 	}
 }
+
+func learnerCheck(srv ServerHealth) func(ctx context.Context) error {
+	return func(ctx context.Context) error {
+		if srv.IsLearner() {
+			return fmt.Errorf("not supported for learner")
+		}
+		return nil
+	}
+}
diff --git a/server/etcdserver/api/membership/metrics.go b/server/etcdserver/api/membership/metrics.go
index 7eb6fb6b7..ad98dbb05 100644
--- a/server/etcdserver/api/membership/metrics.go
+++ b/server/etcdserver/api/membership/metrics.go
@@ -17,19 +17,22 @@ package membership
 import "github.com/prometheus/client_golang/prometheus"
 
 var (
-	ClusterVersionMetrics = prometheus.NewGaugeVec(prometheus.GaugeOpts{
-		Namespace: "etcd",
-		Subsystem: "cluster",
-		Name:      "version",
-		Help:      "Which version is running. 1 for 'cluster_version' label with current cluster version",
-	},
-		[]string{"cluster_version"})
-	knownPeers = prometheus.NewGaugeVec(prometheus.GaugeOpts{
-		Namespace: "etcd",
-		Subsystem: "network",
-		Name:      "known_peers",
-		Help:      "The current number of known peers.",
-	},
+	ClusterVersionMetrics = prometheus.NewGaugeVec(
+		prometheus.GaugeOpts{
+			Namespace: "etcd",
+			Subsystem: "cluster",
+			Name:      "version",
+			Help:      "Which version is running. 1 for 'cluster_version' label with current cluster version",
+		},
+		[]string{"cluster_version"},
+	)
+	knownPeers = prometheus.NewGaugeVec(
+		prometheus.GaugeOpts{
+			Namespace: "etcd",
+			Subsystem: "network",
+			Name:      "known_peers",
+			Help:      "The current number of known peers.",
+		},
 		[]string{"Local", "Remote"},
 	)
 	isLearner = prometheus.NewGauge(prometheus.GaugeOpts{
diff --git a/server/etcdserver/api/rafthttp/metrics.go b/server/etcdserver/api/rafthttp/metrics.go
index 02fff84be..f9e13e2c8 100644
--- a/server/etcdserver/api/rafthttp/metrics.go
+++ b/server/etcdserver/api/rafthttp/metrics.go
@@ -17,150 +17,165 @@ package rafthttp
 import "github.com/prometheus/client_golang/prometheus"
 
 var (
-	activePeers = prometheus.NewGaugeVec(prometheus.GaugeOpts{
-		Namespace: "etcd",
-		Subsystem: "network",
-		Name:      "active_peers",
-		Help:      "The current number of active peer connections.",
-	},
+	activePeers = prometheus.NewGaugeVec(
+		prometheus.GaugeOpts{
+			Namespace: "etcd",
+			Subsystem: "network",
+			Name:      "active_peers",
+			Help:      "The current number of active peer connections.",
+		},
 		[]string{"Local", "Remote"},
 	)
 
-	disconnectedPeers = prometheus.NewCounterVec(prometheus.CounterOpts{
-		Namespace: "etcd",
-		Subsystem: "network",
-		Name:      "disconnected_peers_total",
-		Help:      "The total number of disconnected peers.",
-	},
+	disconnectedPeers = prometheus.NewCounterVec(
+		prometheus.CounterOpts{
+			Namespace: "etcd",
+			Subsystem: "network",
+			Name:      "disconnected_peers_total",
+			Help:      "The total number of disconnected peers.",
+		},
 		[]string{"Local", "Remote"},
 	)
 
-	sentBytes = prometheus.NewCounterVec(prometheus.CounterOpts{
-		Namespace: "etcd",
-		Subsystem: "network",
-		Name:      "peer_sent_bytes_total",
-		Help:      "The total number of bytes sent to peers.",
-	},
+	sentBytes = prometheus.NewCounterVec(
+		prometheus.CounterOpts{
+			Namespace: "etcd",
+			Subsystem: "network",
+			Name:      "peer_sent_bytes_total",
+			Help:      "The total number of bytes sent to peers.",
+		},
 		[]string{"To"},
 	)
 
-	receivedBytes = prometheus.NewCounterVec(prometheus.CounterOpts{
-		Namespace: "etcd",
-		Subsystem: "network",
-		Name:      "peer_received_bytes_total",
-		Help:      "The total number of bytes received from peers.",
-	},
+	receivedBytes = prometheus.NewCounterVec(
+		prometheus.CounterOpts{
+			Namespace: "etcd",
+			Subsystem: "network",
+			Name:      "peer_received_bytes_total",
+			Help:      "The total number of bytes received from peers.",
+		},
 		[]string{"From"},
 	)
 
-	sentFailures = prometheus.NewCounterVec(prometheus.CounterOpts{
-		Namespace: "etcd",
-		Subsystem: "network",
-		Name:      "peer_sent_failures_total",
-		Help:      "The total number of send failures from peers.",
-	},
+	sentFailures = prometheus.NewCounterVec(
+		prometheus.CounterOpts{
+			Namespace: "etcd",
+			Subsystem: "network",
+			Name:      "peer_sent_failures_total",
+			Help:      "The total number of send failures from peers.",
+		},
 		[]string{"To"},
 	)
 
-	recvFailures = prometheus.NewCounterVec(prometheus.CounterOpts{
-		Namespace: "etcd",
-		Subsystem: "network",
-		Name:      "peer_received_failures_total",
-		Help:      "The total number of receive failures from peers.",
-	},
+	recvFailures = prometheus.NewCounterVec(
+		prometheus.CounterOpts{
+			Namespace: "etcd",
+			Subsystem: "network",
+			Name:      "peer_received_failures_total",
+			Help:      "The total number of receive failures from peers.",
+		},
 		[]string{"From"},
 	)
 
-	snapshotSend = prometheus.NewCounterVec(prometheus.CounterOpts{
-		Namespace: "etcd",
-		Subsystem: "network",
-		Name:      "snapshot_send_success",
-		Help:      "Total number of successful snapshot sends",
-	},
+	snapshotSend = prometheus.NewCounterVec(
+		prometheus.CounterOpts{
+			Namespace: "etcd",
+			Subsystem: "network",
+			Name:      "snapshot_send_success",
+			Help:      "Total number of successful snapshot sends",
+		},
 		[]string{"To"},
 	)
 
-	snapshotSendInflights = prometheus.NewGaugeVec(prometheus.GaugeOpts{
-		Namespace: "etcd",
-		Subsystem: "network",
-		Name:      "snapshot_send_inflights_total",
-		Help:      "Total number of inflight snapshot sends",
-	},
+	snapshotSendInflights = prometheus.NewGaugeVec(
+		prometheus.GaugeOpts{
+			Namespace: "etcd",
+			Subsystem: "network",
+			Name:      "snapshot_send_inflights_total",
+			Help:      "Total number of inflight snapshot sends",
+		},
 		[]string{"To"},
 	)
 
-	snapshotSendFailures = prometheus.NewCounterVec(prometheus.CounterOpts{
-		Namespace: "etcd",
-		Subsystem: "network",
-		Name:      "snapshot_send_failures",
-		Help:      "Total number of snapshot send failures",
-	},
+	snapshotSendFailures = prometheus.NewCounterVec(
+		prometheus.CounterOpts{
+			Namespace: "etcd",
+			Subsystem: "network",
+			Name:      "snapshot_send_failures",
+			Help:      "Total number of snapshot send failures",
+		},
 		[]string{"To"},
 	)
 
-	snapshotSendSeconds = prometheus.NewHistogramVec(prometheus.HistogramOpts{
-		Namespace: "etcd",
-		Subsystem: "network",
-		Name:      "snapshot_send_total_duration_seconds",
-		Help:      "Total latency distributions of v3 snapshot sends",
-
-		// lowest bucket start of upper bound 0.1 sec (100 ms) with factor 2
-		// highest bucket start of 0.1 sec * 2^9 == 51.2 sec
-		Buckets: prometheus.ExponentialBuckets(0.1, 2, 10),
-	},
+	snapshotSendSeconds = prometheus.NewHistogramVec(
+		prometheus.HistogramOpts{
+			Namespace: "etcd",
+			Subsystem: "network",
+			Name:      "snapshot_send_total_duration_seconds",
+			Help:      "Total latency distributions of v3 snapshot sends",
+
+			// lowest bucket start of upper bound 0.1 sec (100 ms) with factor 2
+			// highest bucket start of 0.1 sec * 2^9 == 51.2 sec
+			Buckets: prometheus.ExponentialBuckets(0.1, 2, 10),
+		},
 		[]string{"To"},
 	)
 
-	snapshotReceive = prometheus.NewCounterVec(prometheus.CounterOpts{
-		Namespace: "etcd",
-		Subsystem: "network",
-		Name:      "snapshot_receive_success",
-		Help:      "Total number of successful snapshot receives",
-	},
+	snapshotReceive = prometheus.NewCounterVec(
+		prometheus.CounterOpts{
+			Namespace: "etcd",
+			Subsystem: "network",
+			Name:      "snapshot_receive_success",
+			Help:      "Total number of successful snapshot receives",
+		},
 		[]string{"From"},
 	)
 
-	snapshotReceiveInflights = prometheus.NewGaugeVec(prometheus.GaugeOpts{
-		Namespace: "etcd",
-		Subsystem: "network",
-		Name:      "snapshot_receive_inflights_total",
-		Help:      "Total number of inflight snapshot receives",
-	},
+	snapshotReceiveInflights = prometheus.NewGaugeVec(
+		prometheus.GaugeOpts{
+			Namespace: "etcd",
+			Subsystem: "network",
+			Name:      "snapshot_receive_inflights_total",
+			Help:      "Total number of inflight snapshot receives",
+		},
 		[]string{"From"},
 	)
 
-	snapshotReceiveFailures = prometheus.NewCounterVec(prometheus.CounterOpts{
-		Namespace: "etcd",
-		Subsystem: "network",
-		Name:      "snapshot_receive_failures",
-		Help:      "Total number of snapshot receive failures",
-	},
+	snapshotReceiveFailures = prometheus.NewCounterVec(
+		prometheus.CounterOpts{
+			Namespace: "etcd",
+			Subsystem: "network",
+			Name:      "snapshot_receive_failures",
+			Help:      "Total number of snapshot receive failures",
+		},
 		[]string{"From"},
 	)
 
-	snapshotReceiveSeconds = prometheus.NewHistogramVec(prometheus.HistogramOpts{
-		Namespace: "etcd",
-		Subsystem: "network",
-		Name:      "snapshot_receive_total_duration_seconds",
-		Help:      "Total latency distributions of v3 snapshot receives",
-
-		// lowest bucket start of upper bound 0.1 sec (100 ms) with factor 2
-		// highest bucket start of 0.1 sec * 2^9 == 51.2 sec
-		Buckets: prometheus.ExponentialBuckets(0.1, 2, 10),
-	},
+	snapshotReceiveSeconds = prometheus.NewHistogramVec(
+		prometheus.HistogramOpts{
+			Namespace: "etcd",
+			Subsystem: "network",
+			Name:      "snapshot_receive_total_duration_seconds",
+			Help:      "Total latency distributions of v3 snapshot receives",
+
+			// lowest bucket start of upper bound 0.1 sec (100 ms) with factor 2
+			// highest bucket start of 0.1 sec * 2^9 == 51.2 sec
+			Buckets: prometheus.ExponentialBuckets(0.1, 2, 10),
+		},
 		[]string{"From"},
 	)
 
-	rttSec = prometheus.NewHistogramVec(prometheus.HistogramOpts{
-		Namespace: "etcd",
-		Subsystem: "network",
-		Name:      "peer_round_trip_time_seconds",
-		Help:      "Round-Trip-Time histogram between peers",
-
-		// lowest bucket start of upper bound 0.0001 sec (0.1 ms) with factor 2
-		// highest bucket start of 0.0001 sec * 2^15 == 3.2768 sec
-		Buckets: prometheus.ExponentialBuckets(0.0001, 2, 16),
-	},
+	rttSec = prometheus.NewHistogramVec(
+		prometheus.HistogramOpts{
+			Namespace: "etcd",
+			Subsystem: "network",
+			Name:      "peer_round_trip_time_seconds",
+			Help:      "Round-Trip-Time histogram between peers",
+
+			// lowest bucket start of upper bound 0.0001 sec (0.1 ms) with factor 2
+			// highest bucket start of 0.0001 sec * 2^15 == 3.2768 sec
+			Buckets: prometheus.ExponentialBuckets(0.0001, 2, 16),
+		},
 		[]string{"To"},
 	)
 )
diff --git a/server/etcdserver/api/v2store/metrics.go b/server/etcdserver/api/v2store/metrics.go
index 5adea1efd..943457bdf 100644
--- a/server/etcdserver/api/v2store/metrics.go
+++ b/server/etcdserver/api/v2store/metrics.go
@@ -28,7 +28,9 @@ var (
 			Subsystem: "store",
 			Name:      "reads_total",
 			Help:      "Total number of reads action by (get/getRecursive), local to this member.",
-		}, []string{"action"})
+		},
+		[]string{"action"},
+	)
 
 	writeCounter = prometheus.NewCounterVec(
 		prometheus.CounterOpts{
@@ -36,7 +38,9 @@ var (
 			Subsystem: "store",
 			Name:      "writes_total",
 			Help:      "Total number of writes (e.g. set/compareAndDelete) seen by this member.",
-		}, []string{"action"})
+		},
+		[]string{"action"},
+	)
 
 	readFailedCounter = prometheus.NewCounterVec(
 		prometheus.CounterOpts{
@@ -44,7 +48,9 @@ var (
 			Subsystem: "store",
 			Name:      "reads_failed_total",
 			Help:      "Failed read actions by (get/getRecursive), local to this member.",
-		}, []string{"action"})
+		},
+		[]string{"action"},
+	)
 
 	writeFailedCounter = prometheus.NewCounterVec(
 		prometheus.CounterOpts{
@@ -52,7 +58,9 @@ var (
 			Subsystem: "store",
 			Name:      "writes_failed_total",
 			Help:      "Failed write actions (e.g. set/compareAndDelete), seen by this member.",
-		}, []string{"action"})
+		},
+		[]string{"action"},
+	)
 
 	expireCounter = prometheus.NewCounter(
 		prometheus.CounterOpts{
@@ -60,7 +68,8 @@ var (
 			Subsystem: "store",
 			Name:      "expires_total",
 			Help:      "Total number of expired keys.",
-		})
+		},
+	)
 
 	watchRequests = prometheus.NewCounter(
 		prometheus.CounterOpts{
@@ -68,7 +77,8 @@ var (
 			Subsystem: "store",
 			Name:      "watch_requests_total",
 			Help:      "Total number of incoming watch requests (new or reestablished).",
-		})
+		},
+	)
 
 	watcherCount = prometheus.NewGauge(
 		prometheus.GaugeOpts{
@@ -76,7 +86,8 @@ var (
 			Subsystem: "store",
 			Name:      "watchers",
 			Help:      "Count of currently active watchers.",
-		})
+		},
+	)
 )
 
 const (
diff --git a/server/etcdserver/api/v2store/store.go b/server/etcdserver/api/v2store/store.go
index 0c6f7b516..2129bd635 100644
--- a/server/etcdserver/api/v2store/store.go
+++ b/server/etcdserver/api/v2store/store.go
@@ -603,18 +603,17 @@ func (s *store) internalCreate(nodePath string, dir bool, value string, unique,
 
 	// force will try to replace an existing file
 	if n != nil {
-		if replace {
-			if n.IsDir() {
-				return nil, v2error.NewError(v2error.EcodeNotFile, nodePath, currIndex)
-			}
-			e.PrevNode = n.Repr(false, false, s.clock)
-
-			if err := n.Remove(false, false, nil); err != nil {
-				return nil, err
-			}
-		} else {
+		if !replace {
 			return nil, v2error.NewError(v2error.EcodeNodeExist, nodePath, currIndex)
 		}
+		if n.IsDir() {
+			return nil, v2error.NewError(v2error.EcodeNotFile, nodePath, currIndex)
+		}
+		e.PrevNode = n.Repr(false, false, s.clock)
+
+		if err := n.Remove(false, false, nil); err != nil {
+			return nil, err
+		}
 	}
 
 	if !dir { // create file
diff --git a/server/etcdserver/api/v3compactor/periodic.go b/server/etcdserver/api/v3compactor/periodic.go
index 067f69ec3..7468d230b 100644
--- a/server/etcdserver/api/v3compactor/periodic.go
+++ b/server/etcdserver/api/v3compactor/periodic.go
@@ -72,12 +72,12 @@ Compaction period 1-hour:
 	- failure? update revs, and retry after 1/10 of 1-hour (6-minute)
 
 Compaction period 24-hour:
-  1. compute compaction period, which is 1-hour
-  2. record revisions for every 1/10 of 1-hour (6-minute)
+  1. compute compaction period, which is 24-hour
+  2. record revisions for every 1/10 of 24-hour (144-minute)
   3. keep recording revisions with no compaction for first 24-hour
   4. do compact with revs[0]
 	- success? continue on for-loop and move sliding window; revs = revs[1:]
-	- failure? update revs, and retry after 1/10 of 1-hour (6-minute)
+	- failure? update revs, and retry after 1/10 of 24-hour (144-minute)
 
 Compaction period 59-min:
   1. compute compaction period, which is 59-min
diff --git a/server/etcdserver/api/v3rpc/grpc.go b/server/etcdserver/api/v3rpc/grpc.go
index 329492078..810fd219b 100644
--- a/server/etcdserver/api/v3rpc/grpc.go
+++ b/server/etcdserver/api/v3rpc/grpc.go
@@ -18,8 +18,10 @@ import (
 	"crypto/tls"
 	"math"
 
-	grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus"
+	grpc_prometheus "github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus"
+	"github.com/prometheus/client_golang/prometheus"
 	"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
+	"go.uber.org/zap"
 	"google.golang.org/grpc"
 	"google.golang.org/grpc/health"
 	healthpb "google.golang.org/grpc/health/grpc_health_v1"
@@ -39,10 +41,21 @@ func Server(s *etcdserver.EtcdServer, tls *tls.Config, interceptor grpc.UnarySer
 	if tls != nil {
 		opts = append(opts, grpc.Creds(credentials.NewTransportCredential(tls)))
 	}
+
+	var mopts []grpc_prometheus.ServerMetricsOption
+	if s.Cfg.Metrics == "extensive" {
+		mopts = append(mopts, grpc_prometheus.WithServerHandlingTimeHistogram())
+	}
+	serverMetrics := grpc_prometheus.NewServerMetrics(mopts...)
+	err := prometheus.Register(serverMetrics)
+	if err != nil {
+		s.Cfg.Logger.Warn("etcdserver: failed to register grpc metrics", zap.Error(err))
+	}
+
 	chainUnaryInterceptors := []grpc.UnaryServerInterceptor{
 		newLogUnaryInterceptor(s),
 		newUnaryInterceptor(s),
-		grpc_prometheus.UnaryServerInterceptor,
+		serverMetrics.UnaryServerInterceptor(),
 	}
 	if interceptor != nil {
 		chainUnaryInterceptors = append(chainUnaryInterceptors, interceptor)
@@ -50,7 +63,7 @@ func Server(s *etcdserver.EtcdServer, tls *tls.Config, interceptor grpc.UnarySer
 
 	chainStreamInterceptors := []grpc.StreamServerInterceptor{
 		newStreamInterceptor(s),
-		grpc_prometheus.StreamServerInterceptor,
+		serverMetrics.StreamServerInterceptor(),
 	}
 
 	if s.Cfg.ExperimentalEnableDistributedTracing {
@@ -79,7 +92,7 @@ func Server(s *etcdserver.EtcdServer, tls *tls.Config, interceptor grpc.UnarySer
 	pb.RegisterMaintenanceServer(grpcServer, NewMaintenanceServer(s, healthNotifier))
 
 	// set zero values for metrics registered for this grpc server
-	grpc_prometheus.Register(grpcServer)
+	serverMetrics.InitializeMetrics(grpcServer)
 
 	return grpcServer
 }
diff --git a/server/etcdserver/api/v3rpc/metrics.go b/server/etcdserver/api/v3rpc/metrics.go
index a4ee723c5..d79506e07 100644
--- a/server/etcdserver/api/v3rpc/metrics.go
+++ b/server/etcdserver/api/v3rpc/metrics.go
@@ -14,7 +14,9 @@
 
 package v3rpc
 
-import "github.com/prometheus/client_golang/prometheus"
+import (
+	"github.com/prometheus/client_golang/prometheus"
+)
 
 var (
 	sentBytes = prometheus.NewCounter(prometheus.CounterOpts{
@@ -31,21 +33,23 @@ var (
 		Help:      "The total number of bytes received from grpc clients.",
 	})
 
-	streamFailures = prometheus.NewCounterVec(prometheus.CounterOpts{
-		Namespace: "etcd",
-		Subsystem: "network",
-		Name:      "server_stream_failures_total",
-		Help:      "The total number of stream failures from the local server.",
-	},
+	streamFailures = prometheus.NewCounterVec(
+		prometheus.CounterOpts{
+			Namespace: "etcd",
+			Subsystem: "network",
+			Name:      "server_stream_failures_total",
+			Help:      "The total number of stream failures from the local server.",
+		},
 		[]string{"Type", "API"},
 	)
 
-	clientRequests = prometheus.NewCounterVec(prometheus.CounterOpts{
-		Namespace: "etcd",
-		Subsystem: "server",
-		Name:      "client_requests_total",
-		Help:      "The total number of client requests per client version.",
-	},
+	clientRequests = prometheus.NewCounterVec(
+		prometheus.CounterOpts{
+			Namespace: "etcd",
+			Subsystem: "server",
+			Name:      "client_requests_total",
+			Help:      "The total number of client requests per client version.",
+		},
 		[]string{"type", "client_api_version"},
 	)
 )
diff --git a/server/etcdserver/apply/metrics.go b/server/etcdserver/apply/metrics.go
index c6b438646..cafbdc797 100644
--- a/server/etcdserver/apply/metrics.go
+++ b/server/etcdserver/apply/metrics.go
@@ -16,13 +16,15 @@ package apply
 
 import "github.com/prometheus/client_golang/prometheus"
 
-var alarms = prometheus.NewGaugeVec(prometheus.GaugeOpts{
-	Namespace: "etcd_debugging",
-	Subsystem: "server",
-	Name:      "alarms",
-	Help:      "Alarms for every member in cluster. 1 for 'server_id' label with current ID. 2 for 'alarm_type' label with type of this alarm",
-},
-	[]string{"server_id", "alarm_type"})
+var alarms = prometheus.NewGaugeVec(
+	prometheus.GaugeOpts{
+		Namespace: "etcd_debugging",
+		Subsystem: "server",
+		Name:      "alarms",
+		Help:      "Alarms for every member in cluster. 1 for 'server_id' label with current ID. 2 for 'alarm_type' label with type of this alarm",
+	},
+	[]string{"server_id", "alarm_type"},
+)
 
 func init() {
 	prometheus.MustRegister(alarms)
diff --git a/server/etcdserver/bootstrap.go b/server/etcdserver/bootstrap.go
index 309ed67bb..b25b7f6e1 100644
--- a/server/etcdserver/bootstrap.go
+++ b/server/etcdserver/bootstrap.go
@@ -210,7 +210,7 @@ func bootstrapBackend(cfg config.ServerConfig, haveWAL bool, st v2store.Store, s
 	}()
 	ci.SetBackend(be)
 	schema.CreateMetaBucket(be.BatchTx())
-	if cfg.ExperimentalBootstrapDefragThresholdMegabytes != 0 {
+	if cfg.BootstrapDefragThresholdMegabytes != 0 {
 		err = maybeDefragBackend(cfg, be)
 		if err != nil {
 			return nil, err
@@ -254,7 +254,7 @@ func maybeDefragBackend(cfg config.ServerConfig, be backend.Backend) error {
 	size := be.Size()
 	sizeInUse := be.SizeInUse()
 	freeableMemory := uint(size - sizeInUse)
-	thresholdBytes := cfg.ExperimentalBootstrapDefragThresholdMegabytes * 1024 * 1024
+	thresholdBytes := cfg.BootstrapDefragThresholdMegabytes * 1024 * 1024
 	if freeableMemory < thresholdBytes {
 		cfg.Logger.Info("Skipping defragmentation",
 			zap.Int64("current-db-size-bytes", size),
@@ -290,7 +290,7 @@ func bootstrapExistingClusterNoWAL(cfg config.ServerConfig, prt http.RoundTrippe
 	if err := cfg.VerifyJoinExisting(); err != nil {
 		return nil, err
 	}
-	cl, err := membership.NewClusterFromURLsMap(cfg.Logger, cfg.InitialClusterToken, cfg.InitialPeerURLsMap, membership.WithMaxLearners(cfg.ExperimentalMaxLearners))
+	cl, err := membership.NewClusterFromURLsMap(cfg.Logger, cfg.InitialClusterToken, cfg.InitialPeerURLsMap, membership.WithMaxLearners(cfg.MaxLearners))
 	if err != nil {
 		return nil, err
 	}
@@ -305,7 +305,7 @@ func bootstrapExistingClusterNoWAL(cfg config.ServerConfig, prt http.RoundTrippe
 		return nil, fmt.Errorf("incompatible with current running cluster")
 	}
 	scaleUpLearners := false
-	if err := membership.ValidateMaxLearnerConfig(cfg.ExperimentalMaxLearners, existingCluster.Members(), scaleUpLearners); err != nil {
+	if err := membership.ValidateMaxLearnerConfig(cfg.MaxLearners, existingCluster.Members(), scaleUpLearners); err != nil {
 		return nil, err
 	}
 	remotes := existingCluster.Members()
@@ -322,7 +322,7 @@ func bootstrapNewClusterNoWAL(cfg config.ServerConfig, prt http.RoundTripper) (*
 	if err := cfg.VerifyBootstrap(); err != nil {
 		return nil, err
 	}
-	cl, err := membership.NewClusterFromURLsMap(cfg.Logger, cfg.InitialClusterToken, cfg.InitialPeerURLsMap, membership.WithMaxLearners(cfg.ExperimentalMaxLearners))
+	cl, err := membership.NewClusterFromURLsMap(cfg.Logger, cfg.InitialClusterToken, cfg.InitialPeerURLsMap, membership.WithMaxLearners(cfg.MaxLearners))
 	if err != nil {
 		return nil, err
 	}
@@ -350,7 +350,7 @@ func bootstrapNewClusterNoWAL(cfg config.ServerConfig, prt http.RoundTripper) (*
 		if config.CheckDuplicateURL(urlsmap) {
 			return nil, fmt.Errorf("discovery cluster %s has duplicate url", urlsmap)
 		}
-		if cl, err = membership.NewClusterFromURLsMap(cfg.Logger, cfg.InitialClusterToken, urlsmap, membership.WithMaxLearners(cfg.ExperimentalMaxLearners)); err != nil {
+		if cl, err = membership.NewClusterFromURLsMap(cfg.Logger, cfg.InitialClusterToken, urlsmap, membership.WithMaxLearners(cfg.MaxLearners)); err != nil {
 			return nil, err
 		}
 	}
@@ -372,10 +372,10 @@ func bootstrapClusterWithWAL(cfg config.ServerConfig, meta *snapshotMetadata) (*
 			zap.String("wal-dir", cfg.WALDir()),
 		)
 	}
-	cl := membership.NewCluster(cfg.Logger, membership.WithMaxLearners(cfg.ExperimentalMaxLearners))
+	cl := membership.NewCluster(cfg.Logger, membership.WithMaxLearners(cfg.MaxLearners))
 
 	scaleUpLearners := false
-	if err := membership.ValidateMaxLearnerConfig(cfg.ExperimentalMaxLearners, cl.Members(), scaleUpLearners); err != nil {
+	if err := membership.ValidateMaxLearnerConfig(cfg.MaxLearners, cl.Members(), scaleUpLearners); err != nil {
 		return nil, err
 	}
 
@@ -457,7 +457,7 @@ func (c *bootstrappedCluster) Finalize(cfg config.ServerConfig, s *bootstrappedS
 		}
 	}
 	scaleUpLearners := false
-	return membership.ValidateMaxLearnerConfig(cfg.ExperimentalMaxLearners, c.cl.Members(), scaleUpLearners)
+	return membership.ValidateMaxLearnerConfig(cfg.MaxLearners, c.cl.Members(), scaleUpLearners)
 }
 
 func (c *bootstrappedCluster) databaseFileMissing(s *bootstrappedStorage) bool {
diff --git a/server/etcdserver/cluster_util.go b/server/etcdserver/cluster_util.go
index 089d2acf7..425ed971c 100644
--- a/server/etcdserver/cluster_util.go
+++ b/server/etcdserver/cluster_util.go
@@ -353,13 +353,12 @@ func getDowngradeEnabledFromRemotePeers(lg *zap.Logger, cl *membership.RaftClust
 			continue
 		}
 		enable, err := getDowngradeEnabled(lg, m, rt, timeout)
-		if err != nil {
-			lg.Warn("failed to get downgrade enabled status", zap.String("remote-member-id", m.ID.String()), zap.Error(err))
-		} else {
+		if err == nil {
 			// Since the "/downgrade/enabled" serves linearized data,
 			// this function can return once it gets a non-error response from the endpoint.
 			return enable
 		}
+		lg.Warn("failed to get downgrade enabled status", zap.String("remote-member-id", m.ID.String()), zap.Error(err))
 	}
 	return false
 }
diff --git a/server/etcdserver/metrics.go b/server/etcdserver/metrics.go
index 4be44a57a..5f3c2f513 100644
--- a/server/etcdserver/metrics.go
+++ b/server/etcdserver/metrics.go
@@ -44,12 +44,13 @@ var (
 		Name:      "leader_changes_seen_total",
 		Help:      "The number of leader changes seen.",
 	})
-	learnerPromoteFailed = prometheus.NewCounterVec(prometheus.CounterOpts{
-		Namespace: "etcd",
-		Subsystem: "server",
-		Name:      "learner_promote_failures",
-		Help:      "The total number of failed learner promotions (likely learner not ready) while this member is leader.",
-	},
+	learnerPromoteFailed = prometheus.NewCounterVec(
+		prometheus.CounterOpts{
+			Namespace: "etcd",
+			Subsystem: "server",
+			Name:      "learner_promote_failures",
+			Help:      "The total number of failed learner promotions (likely learner not ready) while this member is leader.",
+		},
 		[]string{"Reason"},
 	)
 	learnerPromoteSucceed = prometheus.NewCounter(prometheus.CounterOpts{
@@ -112,29 +113,33 @@ var (
 		Name:      "lease_expired_total",
 		Help:      "The total number of expired leases.",
 	})
-
-	currentVersion = prometheus.NewGaugeVec(prometheus.GaugeOpts{
-		Namespace: "etcd",
-		Subsystem: "server",
-		Name:      "version",
-		Help:      "Which version is running. 1 for 'server_version' label with current version.",
-	},
-		[]string{"server_version"})
-	currentGoVersion = prometheus.NewGaugeVec(prometheus.GaugeOpts{
-		Namespace: "etcd",
-		Subsystem: "server",
-		Name:      "go_version",
-		Help:      "Which Go version server is running with. 1 for 'server_go_version' label with current version.",
-	},
-		[]string{"server_go_version"})
-	serverID = prometheus.NewGaugeVec(prometheus.GaugeOpts{
-		Namespace: "etcd",
-		Subsystem: "server",
-		Name:      "id",
-		Help:      "Server or member ID in hexadecimal format. 1 for 'server_id' label with current ID.",
-	},
-		[]string{"server_id"})
-
+	currentVersion = prometheus.NewGaugeVec(
+		prometheus.GaugeOpts{
+			Namespace: "etcd",
+			Subsystem: "server",
+			Name:      "version",
+			Help:      "Which version is running. 1 for 'server_version' label with current version.",
+		},
+		[]string{"server_version"},
+	)
+	currentGoVersion = prometheus.NewGaugeVec(
+		prometheus.GaugeOpts{
+			Namespace: "etcd",
+			Subsystem: "server",
+			Name:      "go_version",
+			Help:      "Which Go version server is running with. 1 for 'server_go_version' label with current version.",
+		},
+		[]string{"server_go_version"},
+	)
+	serverID = prometheus.NewGaugeVec(
+		prometheus.GaugeOpts{
+			Namespace: "etcd",
+			Subsystem: "server",
+			Name:      "id",
+			Help:      "Server or member ID in hexadecimal format. 1 for 'server_id' label with current ID.",
+		},
+		[]string{"server_id"},
+	)
 	fdUsed = prometheus.NewGauge(prometheus.GaugeOpts{
 		Namespace: "os",
 		Subsystem: "fd",
diff --git a/server/etcdserver/server.go b/server/etcdserver/server.go
index e6cee5b26..b1ebd896b 100644
--- a/server/etcdserver/server.go
+++ b/server/etcdserver/server.go
@@ -2006,7 +2006,7 @@ func (s *EtcdServer) applyEntryNormal(e *raftpb.Entry, shouldApplyV3 membership.
 }
 
 func (s *EtcdServer) applyInternalRaftRequest(r *pb.InternalRaftRequest, shouldApplyV3 membership.ShouldApplyV3) *apply.Result {
-	if r.ClusterVersionSet == nil && r.ClusterMemberAttrSet == nil && r.DowngradeInfoSet == nil {
+	if r.ClusterVersionSet == nil && r.ClusterMemberAttrSet == nil && r.DowngradeInfoSet == nil && r.DowngradeVersionTest == nil {
 		if !shouldApplyV3 {
 			return nil
 		}
@@ -2029,6 +2029,11 @@ func (s *EtcdServer) applyInternalRaftRequest(r *pb.InternalRaftRequest, shouldA
 	case r.DowngradeInfoSet != nil:
 		op = "DowngradeInfoSet" // Implemented in 3.5.x
 		membershipApplier.DowngradeInfoSet(r.DowngradeInfoSet, shouldApplyV3)
+	case r.DowngradeVersionTest != nil:
+		op = "DowngradeVersionTest" // Implemented in 3.6 for test only
+		// do nothing, we are just to ensure etcdserver don't panic in case
+		// users(test cases) intentionally inject DowngradeVersionTestRequest
+		// into the WAL files.
 	default:
 		s.lg.Panic("not implemented apply", zap.Stringer("raft-request", r))
 		return nil
diff --git a/server/etcdserver/txn/metrics.go b/server/etcdserver/txn/metrics.go
index e66254b1c..93f2e0753 100644
--- a/server/etcdserver/txn/metrics.go
+++ b/server/etcdserver/txn/metrics.go
@@ -28,28 +28,32 @@ var (
 		Name:      "slow_apply_total",
 		Help:      "The total number of slow apply requests (likely overloaded from slow disk).",
 	})
-	applySec = prometheus.NewHistogramVec(prometheus.HistogramOpts{
-		Namespace: "etcd",
-		Subsystem: "server",
-		Name:      "apply_duration_seconds",
-		Help:      "The latency distributions of v2 apply called by backend.",
+	applySec = prometheus.NewHistogramVec(
+		prometheus.HistogramOpts{
+			Namespace: "etcd",
+			Subsystem: "server",
+			Name:      "apply_duration_seconds",
+			Help:      "The latency distributions of v2 apply called by backend.",
 
-		// lowest bucket start of upper bound 0.0001 sec (0.1 ms) with factor 2
-		// highest bucket start of 0.0001 sec * 2^19 == 52.4288 sec
-		Buckets: prometheus.ExponentialBuckets(0.0001, 2, 20),
-	},
-		[]string{"version", "op", "success"})
-	rangeSec = prometheus.NewHistogramVec(prometheus.HistogramOpts{
-		Namespace: "etcd",
-		Subsystem: "server",
-		Name:      "range_duration_seconds",
-		Help:      "The latency distributions of txn.Range",
+			// lowest bucket start of upper bound 0.0001 sec (0.1 ms) with factor 2
+			// highest bucket start of 0.0001 sec * 2^19 == 52.4288 sec
+			Buckets: prometheus.ExponentialBuckets(0.0001, 2, 20),
+		},
+		[]string{"version", "op", "success"},
+	)
+	rangeSec = prometheus.NewHistogramVec(
+		prometheus.HistogramOpts{
+			Namespace: "etcd",
+			Subsystem: "server",
+			Name:      "range_duration_seconds",
+			Help:      "The latency distributions of txn.Range",
 
-		// lowest bucket start of upper bound 0.0001 sec (0.1 ms) with factor 2
-		// highest bucket start of 0.0001 sec * 2^19 == 52.4288 sec
-		Buckets: prometheus.ExponentialBuckets(0.0001, 2, 20),
-	},
-		[]string{"success"})
+			// lowest bucket start of upper bound 0.0001 sec (0.1 ms) with factor 2
+			// highest bucket start of 0.0001 sec * 2^19 == 52.4288 sec
+			Buckets: prometheus.ExponentialBuckets(0.0001, 2, 20),
+		},
+		[]string{"success"},
+	)
 )
 
 func ApplySecObserve(version, op string, success bool, latency time.Duration) {
diff --git a/server/go.mod b/server/go.mod
index 8a21f19ec..40556abb8 100644
--- a/server/go.mod
+++ b/server/go.mod
@@ -9,14 +9,14 @@ require (
 	github.com/coreos/go-systemd/v22 v22.5.0
 	github.com/dustin/go-humanize v1.0.1
 	github.com/gogo/protobuf v1.3.2
-	github.com/golang-jwt/jwt/v4 v4.5.1
+	github.com/golang-jwt/jwt/v5 v5.2.1
 	github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da
 	github.com/golang/protobuf v1.5.4
 	github.com/google/btree v1.1.3
 	github.com/google/go-cmp v0.6.0
 	github.com/grpc-ecosystem/go-grpc-middleware v1.3.0
-	github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0
-	github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1
+	github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1
+	github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.0
 	github.com/jonboulle/clockwork v0.5.0
 	github.com/prometheus/client_golang v1.20.5
 	github.com/prometheus/client_model v0.6.1
@@ -32,17 +32,17 @@ require (
 	go.etcd.io/etcd/client/v3 v3.6.0-alpha.0
 	go.etcd.io/etcd/pkg/v3 v3.6.0-alpha.0
 	go.etcd.io/raft/v3 v3.6.0-beta.0
-	go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.58.0
-	go.opentelemetry.io/otel v1.33.0
-	go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0
-	go.opentelemetry.io/otel/sdk v1.33.0
+	go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0
+	go.opentelemetry.io/otel v1.34.0
+	go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0
+	go.opentelemetry.io/otel/sdk v1.34.0
 	go.uber.org/zap v1.27.0
 	golang.org/x/crypto v0.32.0
 	golang.org/x/net v0.34.0
 	golang.org/x/time v0.9.0
-	google.golang.org/genproto/googleapis/api v0.0.0-20250102185135-69823020774d
-	google.golang.org/grpc v1.69.4
-	google.golang.org/protobuf v1.36.2
+	google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f
+	google.golang.org/grpc v1.70.0
+	google.golang.org/protobuf v1.36.4
 	gopkg.in/natefinch/lumberjack.v2 v2.2.1
 	sigs.k8s.io/yaml v1.4.0
 )
@@ -56,24 +56,25 @@ require (
 	github.com/go-logr/stdr v1.2.2 // indirect
 	github.com/google/uuid v1.6.0 // indirect
 	github.com/gorilla/websocket v1.4.2 // indirect
+	github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 // indirect
 	github.com/inconshreveable/mousetrap v1.1.0 // indirect
 	github.com/klauspost/compress v1.17.9 // indirect
 	github.com/kylelemons/godebug v1.1.0 // indirect
 	github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
 	github.com/pmezard/go-difflib v1.0.0 // indirect
-	github.com/prometheus/common v0.61.0 // indirect
+	github.com/prometheus/common v0.62.0 // indirect
 	github.com/prometheus/procfs v0.15.1 // indirect
 	github.com/sirupsen/logrus v1.9.3 // indirect
 	github.com/spf13/pflag v1.0.5 // indirect
 	go.opentelemetry.io/auto/sdk v1.1.0 // indirect
-	go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0 // indirect
-	go.opentelemetry.io/otel/metric v1.33.0 // indirect
-	go.opentelemetry.io/otel/trace v1.33.0 // indirect
+	go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 // indirect
+	go.opentelemetry.io/otel/metric v1.34.0 // indirect
+	go.opentelemetry.io/otel/trace v1.34.0 // indirect
 	go.opentelemetry.io/proto/otlp v1.5.0 // indirect
 	go.uber.org/multierr v1.11.0 // indirect
 	golang.org/x/sys v0.29.0 // indirect
 	golang.org/x/text v0.21.0 // indirect
-	google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d // indirect
+	google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
 	sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6 // indirect
 )
diff --git a/server/go.sum b/server/go.sum
index aabc834e1..ed7ac3830 100644
--- a/server/go.sum
+++ b/server/go.sum
@@ -36,8 +36,8 @@ github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/me
 github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
 github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
 github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
-github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo=
-github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
+github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
+github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
 github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
 github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@@ -59,10 +59,12 @@ github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0U
 github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
 github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw=
 github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y=
-github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho=
-github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
-github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 h1:VNqngBF40hVlDloBruUehVYC3ArSgIyScOAyMRqBxRg=
-github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1/go.mod h1:RBRO7fro65R6tjKzYgLAFo0t1QEXY1Dp+i/bvpRiqiQ=
+github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 h1:qnpSQwGEnkcRpTqNOIR6bJbR0gAorgP9CSALpRcKoAA=
+github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1/go.mod h1:lXGCsh6c22WGtjr+qGHj1otzZpV/1kwTMAqkwZsnWRU=
+github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 h1:pRhl55Yx1eC7BZ1N+BBWwnKaMyD8uC+34TLdndZMAKk=
+github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0/go.mod h1:XKMd7iuf/RGPSMJ/U4HP0zS2Z9Fh8Ps9a+6X26m/tmI=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.0 h1:VD1gqscl4nYs1YxVuSdemTrSgTKrwOWDK0FVFMqm+Cg=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.0/go.mod h1:4EgsQoS4TOhJizV+JTFg40qx1Ofh3XmXEQNBpgvNT40=
 github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
 github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
 github.com/jonboulle/clockwork v0.5.0 h1:Hyh9A8u51kptdkR+cqRpT1EebBwTn1oK9YfGYbdFz6I=
@@ -90,8 +92,8 @@ github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/j
 github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
 github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
 github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
-github.com/prometheus/common v0.61.0 h1:3gv/GThfX0cV2lpO7gkTUwZru38mxevy90Bj8YFSRQQ=
-github.com/prometheus/common v0.61.0/go.mod h1:zr29OCN/2BsJRaFwG8QOBr41D6kkchKbpeNH7pAjb/s=
+github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
+github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
 github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
 github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
 github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
@@ -125,22 +127,22 @@ go.etcd.io/raft/v3 v3.6.0-beta.0 h1:MZFQVjCQxPJj5K9oS69Y+atNvYnGNyOQBnroTdw56jQ=
 go.etcd.io/raft/v3 v3.6.0-beta.0/go.mod h1:C2JoekRXfvImSrk5GnqD0aZ3a+cGVRnyem9qqn2DCEw=
 go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
 go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
-go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.58.0 h1:PS8wXpbyaDJQ2VDHHncMe9Vct0Zn1fEjpsjrLxGJoSc=
-go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.58.0/go.mod h1:HDBUsEjOuRC0EzKZ1bSaRGZWUBAzo+MhAcUUORSr4D0=
-go.opentelemetry.io/otel v1.33.0 h1:/FerN9bax5LoK51X/sI0SVYrjSE0/yUL7DpxW4K3FWw=
-go.opentelemetry.io/otel v1.33.0/go.mod h1:SUUkR6csvUQl+yjReHu5uM3EtVV7MBm5FHKRlNx4I8I=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0 h1:Vh5HayB/0HHfOQA7Ctx69E/Y/DcQSMPpKANYVMQ7fBA=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0/go.mod h1:cpgtDBaqD/6ok/UG0jT15/uKjAY8mRA53diogHBg3UI=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0 h1:5pojmb1U1AogINhN3SurB+zm/nIcusopeBNp42f45QM=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0/go.mod h1:57gTHJSE5S1tqg+EKsLPlTWhpHMsWlVmer+LA926XiA=
-go.opentelemetry.io/otel/metric v1.33.0 h1:r+JOocAyeRVXD8lZpjdQjzMadVZp2M4WmQ+5WtEnklQ=
-go.opentelemetry.io/otel/metric v1.33.0/go.mod h1:L9+Fyctbp6HFTddIxClbQkjtubW6O9QS3Ann/M82u6M=
-go.opentelemetry.io/otel/sdk v1.33.0 h1:iax7M131HuAm9QkZotNHEfstof92xM+N8sr3uHXc2IM=
-go.opentelemetry.io/otel/sdk v1.33.0/go.mod h1:A1Q5oi7/9XaMlIWzPSxLRWOI8nG3FnzHJNbiENQuihM=
-go.opentelemetry.io/otel/sdk/metric v1.31.0 h1:i9hxxLJF/9kkvfHppyLL55aW7iIJz4JjxTeYusH7zMc=
-go.opentelemetry.io/otel/sdk/metric v1.31.0/go.mod h1:CRInTMVvNhUKgSAMbKyTMxqOBC0zgyxzW55lZzX43Y8=
-go.opentelemetry.io/otel/trace v1.33.0 h1:cCJuF7LRjUFso9LPnEAHJDB2pqzp+hbO8eu1qqW2d/s=
-go.opentelemetry.io/otel/trace v1.33.0/go.mod h1:uIcdVUZMpTAmz0tI1z04GoVSezK37CbGV4fr1f2nBck=
+go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0 h1:rgMkmiGfix9vFJDcDi1PK8WEQP4FLQwLDfhp5ZLpFeE=
+go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0/go.mod h1:ijPqXp5P6IRRByFVVg9DY8P5HkxkHE5ARIa+86aXPf4=
+go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY=
+go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 h1:OeNbIYk/2C15ckl7glBlOBp5+WlYsOElzTNmiPW/x60=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0/go.mod h1:7Bept48yIeqxP2OZ9/AqIpYS94h2or0aB4FypJTc8ZM=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 h1:tgJ0uaNS4c98WRNUEx5U3aDlrDOI5Rs+1Vifcw4DJ8U=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0/go.mod h1:U7HYyW0zt/a9x5J1Kjs+r1f/d4ZHnYFclhYY2+YbeoE=
+go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ=
+go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE=
+go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A=
+go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU=
+go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU=
+go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ=
+go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k=
+go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE=
 go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4=
 go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4=
 go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
@@ -213,19 +215,19 @@ google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
 google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
 google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
 google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
-google.golang.org/genproto/googleapis/api v0.0.0-20250102185135-69823020774d h1:H8tOf8XM88HvKqLTxe755haY6r1fqqzLbEnfrmLXlSA=
-google.golang.org/genproto/googleapis/api v0.0.0-20250102185135-69823020774d/go.mod h1:2v7Z7gP2ZUOGsaFyxATQSRoBnKygqVq2Cwnvom7QiqY=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d h1:xJJRGY7TJcvIlpSrN3K6LAWgNFUILlO+OMAqtg9aqnw=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d/go.mod h1:3ENsm/5D1mzDyhpzeRi1NR784I0BcofWBoSc5QqqMK4=
+google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f h1:gap6+3Gk41EItBuyi4XX/bp4oqJ3UwuIMl25yGinuAA=
+google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:Ic02D47M+zbarjYYUlK57y316f2MoN0gjAwI3f2S95o=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f h1:OxYkA3wjPsZyBylwymxSHa7ViiW1Sml4ToBrncvFehI=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:+2Yz8+CLJbIfL9z73EW45avw8Lmge3xVElCP9zEKi50=
 google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
 google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
 google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
 google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
-google.golang.org/grpc v1.69.4 h1:MF5TftSMkd8GLw/m0KM6V8CMOCY6NZ1NQDPGFgbTt4A=
-google.golang.org/grpc v1.69.4/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4=
-google.golang.org/protobuf v1.36.2 h1:R8FeyR1/eLmkutZOM5CWghmo5itiG9z0ktFlTVLuTmU=
-google.golang.org/protobuf v1.36.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
+google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ=
+google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw=
+google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM=
+google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
diff --git a/server/lease/metrics.go b/server/lease/metrics.go
index 06f8b5801..e38105e86 100644
--- a/server/lease/metrics.go
+++ b/server/lease/metrics.go
@@ -48,7 +48,8 @@ var (
 			Help:      "Bucketed histogram of lease TTLs.",
 			// 1 second -> 3 months
 			Buckets: prometheus.ExponentialBuckets(1, 2, 24),
-		})
+		},
+	)
 )
 
 func init() {
diff --git a/server/proxy/grpcproxy/watcher.go b/server/proxy/grpcproxy/watcher.go
index 45d3a5352..08e0ab94c 100644
--- a/server/proxy/grpcproxy/watcher.go
+++ b/server/proxy/grpcproxy/watcher.go
@@ -71,12 +71,11 @@ func (w *watcher) send(wr clientv3.WatchResponse) {
 		ev := (*mvccpb.Event)(wr.Events[i])
 		if ev.Kv.ModRevision < w.nextrev {
 			continue
-		} else {
-			// We cannot update w.rev here.
-			// txn can have multiple events with the same rev.
-			// If w.nextrev updates here, it would skip events in the same txn.
-			lastRev = ev.Kv.ModRevision
 		}
+		// We cannot update w.rev here.
+		// txn can have multiple events with the same rev.
+		// If w.nextrev updates here, it would skip events in the same txn.
+		lastRev = ev.Kv.ModRevision
 
 		filtered := false
 		for _, filter := range w.filters {
diff --git a/server/storage/mvcc/key_index.go b/server/storage/mvcc/key_index.go
index 27b22fd48..140351974 100644
--- a/server/storage/mvcc/key_index.go
+++ b/server/storage/mvcc/key_index.go
@@ -116,6 +116,15 @@ func (ki *keyIndex) restore(lg *zap.Logger, created, modified Revision, ver int6
 	keysGauge.Inc()
 }
 
+// restoreTombstone is used to restore a tombstone revision, which is the only
+// revision so far for a key. We don't know the creating revision (i.e. already
+// compacted) of the key, so set it empty.
+func (ki *keyIndex) restoreTombstone(lg *zap.Logger, main, sub int64) {
+	ki.restore(lg, Revision{}, Revision{main, sub}, 1)
+	ki.generations = append(ki.generations, generation{})
+	keysGauge.Dec()
+}
+
 // tombstone puts a revision, pointing to a tombstone, to the keyIndex.
 // It also creates a new empty generation in the keyIndex.
 // It returns ErrRevisionNotFound when tombstone on an empty generation.
diff --git a/server/storage/mvcc/kvstore.go b/server/storage/mvcc/kvstore.go
index 3e1226c91..44c2a6252 100644
--- a/server/storage/mvcc/kvstore.go
+++ b/server/storage/mvcc/kvstore.go
@@ -473,8 +473,12 @@ func restoreIntoIndex(lg *zap.Logger, idx index) (chan<- revKeyValue, <-chan int
 					continue
 				}
 				ki.put(lg, rev.Main, rev.Sub)
-			} else if !isTombstone(rkv.key) {
-				ki.restore(lg, Revision{Main: rkv.kv.CreateRevision}, rev, rkv.kv.Version)
+			} else {
+				if isTombstone(rkv.key) {
+					ki.restoreTombstone(lg, rev.Main, rev.Sub)
+				} else {
+					ki.restore(lg, Revision{Main: rkv.kv.CreateRevision}, rev, rkv.kv.Version)
+				}
 				idx.Insert(ki)
 				kiCache[rkv.kstr] = ki
 			}
diff --git a/server/storage/mvcc/metrics.go b/server/storage/mvcc/metrics.go
index b75abbcc0..e06088478 100644
--- a/server/storage/mvcc/metrics.go
+++ b/server/storage/mvcc/metrics.go
@@ -27,7 +27,8 @@ var (
 			Subsystem: "mvcc",
 			Name:      "range_total",
 			Help:      "Total number of ranges seen by this member.",
-		})
+		},
+	)
 
 	putCounter = prometheus.NewCounter(
 		prometheus.CounterOpts{
@@ -35,7 +36,8 @@ var (
 			Subsystem: "mvcc",
 			Name:      "put_total",
 			Help:      "Total number of puts seen by this member.",
-		})
+		},
+	)
 
 	deleteCounter = prometheus.NewCounter(
 		prometheus.CounterOpts{
@@ -43,7 +45,8 @@ var (
 			Subsystem: "mvcc",
 			Name:      "delete_total",
 			Help:      "Total number of deletes seen by this member.",
-		})
+		},
+	)
 
 	txnCounter = prometheus.NewCounter(
 		prometheus.CounterOpts{
@@ -51,7 +54,8 @@ var (
 			Subsystem: "mvcc",
 			Name:      "txn_total",
 			Help:      "Total number of txns seen by this member.",
-		})
+		},
+	)
 
 	keysGauge = prometheus.NewGauge(
 		prometheus.GaugeOpts{
@@ -59,7 +63,8 @@ var (
 			Subsystem: "mvcc",
 			Name:      "keys_total",
 			Help:      "Total number of keys.",
-		})
+		},
+	)
 
 	watchStreamGauge = prometheus.NewGauge(
 		prometheus.GaugeOpts{
@@ -67,7 +72,8 @@ var (
 			Subsystem: "mvcc",
 			Name:      "watch_stream_total",
 			Help:      "Total number of watch streams.",
-		})
+		},
+	)
 
 	watcherGauge = prometheus.NewGauge(
 		prometheus.GaugeOpts{
@@ -75,7 +81,8 @@ var (
 			Subsystem: "mvcc",
 			Name:      "watcher_total",
 			Help:      "Total number of watchers.",
-		})
+		},
+	)
 
 	slowWatcherGauge = prometheus.NewGauge(
 		prometheus.GaugeOpts{
@@ -83,7 +90,8 @@ var (
 			Subsystem: "mvcc",
 			Name:      "slow_watcher_total",
 			Help:      "Total number of unsynced slow watchers.",
-		})
+		},
+	)
 
 	totalEventsCounter = prometheus.NewCounter(
 		prometheus.CounterOpts{
@@ -91,7 +99,8 @@ var (
 			Subsystem: "mvcc",
 			Name:      "events_total",
 			Help:      "Total number of events sent by this member.",
-		})
+		},
+	)
 
 	pendingEventsGauge = prometheus.NewGauge(
 		prometheus.GaugeOpts{
@@ -99,7 +108,8 @@ var (
 			Subsystem: "mvcc",
 			Name:      "pending_events_total",
 			Help:      "Total number of pending events to be sent.",
-		})
+		},
+	)
 
 	indexCompactionPauseMs = prometheus.NewHistogram(
 		prometheus.HistogramOpts{
@@ -111,7 +121,8 @@ var (
 			// lowest bucket start of upper bound 0.5 ms with factor 2
 			// highest bucket start of 0.5 ms * 2^13 == 4.096 sec
 			Buckets: prometheus.ExponentialBuckets(0.5, 2, 14),
-		})
+		},
+	)
 
 	dbCompactionPauseMs = prometheus.NewHistogram(
 		prometheus.HistogramOpts{
@@ -123,7 +134,8 @@ var (
 			// lowest bucket start of upper bound 1 ms with factor 2
 			// highest bucket start of 1 ms * 2^12 == 4.096 sec
 			Buckets: prometheus.ExponentialBuckets(1, 2, 13),
-		})
+		},
+	)
 
 	dbCompactionTotalMs = prometheus.NewHistogram(
 		prometheus.HistogramOpts{
@@ -135,7 +147,8 @@ var (
 			// lowest bucket start of upper bound 100 ms with factor 2
 			// highest bucket start of 100 ms * 2^13 == 8.192 sec
 			Buckets: prometheus.ExponentialBuckets(100, 2, 14),
-		})
+		},
+	)
 
 	dbCompactionLast = prometheus.NewGauge(
 		prometheus.GaugeOpts{
@@ -143,7 +156,8 @@ var (
 			Subsystem: "mvcc",
 			Name:      "db_compaction_last",
 			Help:      "The unix time of the last db compaction. Resets to 0 on start.",
-		})
+		},
+	)
 
 	dbCompactionKeysCounter = prometheus.NewCounter(
 		prometheus.CounterOpts{
@@ -151,14 +165,16 @@ var (
 			Subsystem: "mvcc",
 			Name:      "db_compaction_keys_total",
 			Help:      "Total number of db keys compacted.",
-		})
+		},
+	)
 
-	dbTotalSize = prometheus.NewGaugeFunc(prometheus.GaugeOpts{
-		Namespace: "etcd",
-		Subsystem: "mvcc",
-		Name:      "db_total_size_in_bytes",
-		Help:      "Total size of the underlying database physically allocated in bytes.",
-	},
+	dbTotalSize = prometheus.NewGaugeFunc(
+		prometheus.GaugeOpts{
+			Namespace: "etcd",
+			Subsystem: "mvcc",
+			Name:      "db_total_size_in_bytes",
+			Help:      "Total size of the underlying database physically allocated in bytes.",
+		},
 		func() float64 {
 			reportDbTotalSizeInBytesMu.RLock()
 			defer reportDbTotalSizeInBytesMu.RUnlock()
@@ -169,12 +185,13 @@ var (
 	reportDbTotalSizeInBytesMu sync.RWMutex
 	reportDbTotalSizeInBytes   = func() float64 { return 0 }
 
-	dbTotalSizeInUse = prometheus.NewGaugeFunc(prometheus.GaugeOpts{
-		Namespace: "etcd",
-		Subsystem: "mvcc",
-		Name:      "db_total_size_in_use_in_bytes",
-		Help:      "Total size of the underlying database logically in use in bytes.",
-	},
+	dbTotalSizeInUse = prometheus.NewGaugeFunc(
+		prometheus.GaugeOpts{
+			Namespace: "etcd",
+			Subsystem: "mvcc",
+			Name:      "db_total_size_in_use_in_bytes",
+			Help:      "Total size of the underlying database logically in use in bytes.",
+		},
 		func() float64 {
 			reportDbTotalSizeInUseInBytesMu.RLock()
 			defer reportDbTotalSizeInUseInBytesMu.RUnlock()
@@ -185,12 +202,13 @@ var (
 	reportDbTotalSizeInUseInBytesMu sync.RWMutex
 	reportDbTotalSizeInUseInBytes   = func() float64 { return 0 }
 
-	dbOpenReadTxN = prometheus.NewGaugeFunc(prometheus.GaugeOpts{
-		Namespace: "etcd",
-		Subsystem: "mvcc",
-		Name:      "db_open_read_transactions",
-		Help:      "The number of currently open read transactions",
-	},
+	dbOpenReadTxN = prometheus.NewGaugeFunc(
+		prometheus.GaugeOpts{
+			Namespace: "etcd",
+			Subsystem: "mvcc",
+			Name:      "db_open_read_transactions",
+			Help:      "The number of currently open read transactions",
+		},
 
 		func() float64 {
 			reportDbOpenReadTxNMu.RLock()
@@ -226,12 +244,13 @@ var (
 		Buckets: prometheus.ExponentialBuckets(.01, 2, 15),
 	})
 
-	currentRev = prometheus.NewGaugeFunc(prometheus.GaugeOpts{
-		Namespace: "etcd_debugging",
-		Subsystem: "mvcc",
-		Name:      "current_revision",
-		Help:      "The current revision of store.",
-	},
+	currentRev = prometheus.NewGaugeFunc(
+		prometheus.GaugeOpts{
+			Namespace: "etcd_debugging",
+			Subsystem: "mvcc",
+			Name:      "current_revision",
+			Help:      "The current revision of store.",
+		},
 		func() float64 {
 			reportCurrentRevMu.RLock()
 			defer reportCurrentRevMu.RUnlock()
diff --git a/server/storage/mvcc/watchable_store.go b/server/storage/mvcc/watchable_store.go
index ee47c2c6d..17f4260e4 100644
--- a/server/storage/mvcc/watchable_store.go
+++ b/server/storage/mvcc/watchable_store.go
@@ -290,15 +290,14 @@ func (s *watchableStore) moveVictims() (moved int) {
 		for w, eb := range wb {
 			// watcher has observed the store up to, but not including, w.minRev
 			rev := w.minRev - 1
-			if w.send(WatchResponse{WatchID: w.id, Events: eb.evs, Revision: rev}) {
-				pendingEventsGauge.Add(float64(len(eb.evs)))
-			} else {
+			if !w.send(WatchResponse{WatchID: w.id, Events: eb.evs, Revision: rev}) {
 				if newVictim == nil {
 					newVictim = make(watcherBatch)
 				}
 				newVictim[w] = eb
 				continue
 			}
+			pendingEventsGauge.Add(float64(len(eb.evs)))
 			moved++
 		}
 
diff --git a/server/storage/wal/version.go b/server/storage/wal/version.go
index 985926503..1c0775bde 100644
--- a/server/storage/wal/version.go
+++ b/server/storage/wal/version.go
@@ -112,8 +112,8 @@ func visitEntryData(entryType raftpb.EntryType, data []byte, visitor Visitor) er
 			break
 		}
 		msg = proto.MessageReflect(&raftReq)
-		if raftReq.ClusterVersionSet != nil {
-			ver, err := semver.NewVersion(raftReq.ClusterVersionSet.Ver)
+		if raftReq.DowngradeVersionTest != nil {
+			ver, err := semver.NewVersion(raftReq.DowngradeVersionTest.Ver)
 			if err != nil {
 				return err
 			}
diff --git a/tools/.golangci.yaml b/tools/.golangci.yaml
index afaaa6a91..60925e0a4 100644
--- a/tools/.golangci.yaml
+++ b/tools/.golangci.yaml
@@ -54,6 +54,10 @@ linters-settings: # please keep this alphabetized
         disabled: false
       - name: dot-imports
         disabled: false
+      - name: early-return
+        disabled: false
+        arguments:
+          - "preserveScope"
       - name: error-return
         disabled: false
       - name: error-naming
@@ -74,6 +78,10 @@ linters-settings: # please keep this alphabetized
         disabled: false
       - name: receiver-naming
         disabled: false
+      - name: superfluous-else
+        disabled: false
+        arguments:
+          - "preserveScope"
       - name: time-naming
         disabled: false
       - name: use-any
diff --git a/tools/etcd-dump-logs/raw.go b/tools/etcd-dump-logs/raw.go
index 2c1bed769..1638d149d 100644
--- a/tools/etcd-dump-logs/raw.go
+++ b/tools/etcd-dump-logs/raw.go
@@ -75,10 +75,9 @@ func readRaw(fromIndex *uint64, waldir string, out io.Writer) {
 		} else if errors.Is(err, io.ErrUnexpectedEOF) {
 			fmt.Fprintf(out, "ErrUnexpectedEOF: The last record might be corrupted, error: %v.\n", err)
 			break
-		} else {
-			log.Printf("Error: Reading failed: %v", err)
-			break
 		}
+		log.Printf("Error: Reading failed: %v", err)
+		break
 	}
 }
 
diff --git a/tools/local-tester/README.md b/tools/local-tester/README.md
index cbd39e6f3..433fb54b4 100644
--- a/tools/local-tester/README.md
+++ b/tools/local-tester/README.md
@@ -1,5 +1,8 @@
 # etcd local-tester
 
+> [!WARNING]
+> etcd-local-tester is now deprecated in favor of our much more comprehensive [robustness testing suite](https://github.com/etcd-io/etcd/tree/main/tests/robustness). In a future etcd release this historic tool will be removed as it is no longer maintained.
+
 The etcd local-tester runs a fault injected cluster using local processes. It sets up an etcd cluster with unreliable network bridges on its peer and client interfaces. The cluster runs with a constant stream of `Put` requests to simulate client usage. A fault injection script periodically kills cluster members and disrupts bridge connectivity.
 
 # Requirements
diff --git a/tools/local-tester/bridge.sh b/tools/local-tester/bridge.sh
index 4d2e5f169..9a5d523aa 100755
--- a/tools/local-tester/bridge.sh
+++ b/tools/local-tester/bridge.sh
@@ -1,5 +1,7 @@
 #!/bin/sh
 
+echo "Warning: etcd-local-tester is now deprecated in favor of our robustness testing suite and will be removed in a future release."
+
 exec tools/local-tester/bridge/bridge \
   -delay-accept    \
   -reset-listen    \
diff --git a/tools/mod/go.mod b/tools/mod/go.mod
index a2dd07b06..b7f8eb5e5 100644
--- a/tools/mod/go.mod
+++ b/tools/mod/go.mod
@@ -5,7 +5,7 @@ go 1.23
 toolchain go1.23.5
 
 require (
-	github.com/alexfalkowski/gocovmerge v1.3.17
+	github.com/alexfalkowski/gocovmerge v1.3.18
 	github.com/appscodelabs/license-bill-of-materials v0.0.0-20220707232035-6018e0c5287c
 	github.com/chzchzchz/goword v0.0.0-20170907005317-a9744cb52b03
 	github.com/cloudflare/cfssl v1.6.5
@@ -13,7 +13,7 @@ require (
 	github.com/golangci/golangci-lint v1.63.4
 	github.com/google/addlicense v1.1.1
 	github.com/google/yamlfmt v0.15.0
-	github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1
+	github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.0
 	go.etcd.io/gofail v0.2.0
 	go.etcd.io/protodoc v0.0.0-20180829002748-484ab544e116
 	go.etcd.io/raft/v3 v3.6.0-beta.0
@@ -135,7 +135,7 @@ require (
 	github.com/maratori/testableexamples v1.0.0 // indirect
 	github.com/maratori/testpackage v1.1.1 // indirect
 	github.com/matoous/godox v0.0.0-20230222163458-006bad1f9d26 // indirect
-	github.com/mattn/go-colorable v0.1.13 // indirect
+	github.com/mattn/go-colorable v0.1.14 // indirect
 	github.com/mattn/go-isatty v0.0.20 // indirect
 	github.com/mattn/go-runewidth v0.0.16 // indirect
 	github.com/mattn/go-sqlite3 v1.14.22 // indirect
@@ -155,7 +155,7 @@ require (
 	github.com/polyfloyd/go-errorlint v1.7.0 // indirect
 	github.com/prometheus/client_golang v1.20.5 // indirect
 	github.com/prometheus/client_model v0.6.1 // indirect
-	github.com/prometheus/common v0.61.0 // indirect
+	github.com/prometheus/common v0.62.0 // indirect
 	github.com/prometheus/procfs v0.15.1 // indirect
 	github.com/quasilyte/go-ruleguard v0.4.3-0.20240823090925-0fe6f58b47b1 // indirect
 	github.com/quasilyte/go-ruleguard/dsl v0.3.22 // indirect
@@ -223,11 +223,11 @@ require (
 	golang.org/x/sys v0.29.0 // indirect
 	golang.org/x/term v0.28.0 // indirect
 	golang.org/x/text v0.21.0 // indirect
-	golang.org/x/tools v0.28.0 // indirect
-	google.golang.org/genproto/googleapis/api v0.0.0-20250102185135-69823020774d // indirect
-	google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d // indirect
-	google.golang.org/grpc v1.69.4 // indirect
-	google.golang.org/protobuf v1.36.2 // indirect
+	golang.org/x/tools v0.29.0 // indirect
+	google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f // indirect
+	google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect
+	google.golang.org/grpc v1.70.0 // indirect
+	google.golang.org/protobuf v1.36.4 // indirect
 	gopkg.in/ini.v1 v1.67.0 // indirect
 	gopkg.in/yaml.v2 v2.4.0 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
diff --git a/tools/mod/go.sum b/tools/mod/go.sum
index fc9a33d91..69be16369 100644
--- a/tools/mod/go.sum
+++ b/tools/mod/go.sum
@@ -32,8 +32,8 @@ github.com/alecthomas/go-check-sumtype v0.3.1 h1:u9aUvbGINJxLVXiFvHUlPEaD7VDULsr
 github.com/alecthomas/go-check-sumtype v0.3.1/go.mod h1:A8TSiN3UPRw3laIgWEUOHHLPa6/r9MtoigdlP5h3K/E=
 github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc=
 github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
-github.com/alexfalkowski/gocovmerge v1.3.17 h1:9ovtNsC1Kkv5If24T4d0FJWgdZ0aHVSx8KMahBi/5Qw=
-github.com/alexfalkowski/gocovmerge v1.3.17/go.mod h1:yEWbBk7DnFvHVl9AMJwTZEOwLdnYveEHc1S9SDphQyc=
+github.com/alexfalkowski/gocovmerge v1.3.18 h1:GRJz7uNUHuumvWQtS2zBjDkLdIiCcYE1rfxYYWwYvs8=
+github.com/alexfalkowski/gocovmerge v1.3.18/go.mod h1:TnngCLiVe3kIrJ8v12zHP8aQkRWpjTomZm18uQrRDvE=
 github.com/alexkohler/nakedret/v2 v2.0.5 h1:fP5qLgtwbx9EJE8dGEERT02YwS8En4r9nnZ71RK+EVU=
 github.com/alexkohler/nakedret/v2 v2.0.5/go.mod h1:bF5i0zF2Wo2o4X4USt9ntUWve6JbFv02Ff4vlkmS/VU=
 github.com/alexkohler/prealloc v1.0.0 h1:Hbq0/3fJPQhNkN0dR95AVrr6R7tou91y0uHG5pOcUuw=
@@ -214,8 +214,8 @@ github.com/gostaticanalysis/nilerr v0.1.1/go.mod h1:wZYb6YI5YAxxq0i1+VJbY0s2YONW
 github.com/gostaticanalysis/testutil v0.3.1-0.20210208050101-bfb5c8eec0e4/go.mod h1:D+FIZ+7OahH3ePw/izIEeH5I06eKs1IKI4Xr64/Am3M=
 github.com/gostaticanalysis/testutil v0.5.0 h1:Dq4wT1DdTwTGCQQv3rl3IvD5Ld0E6HiY+3Zh0sUGqw8=
 github.com/gostaticanalysis/testutil v0.5.0/go.mod h1:OLQSbuM6zw2EvCcXTz1lVq5unyoNft372msDY0nY5Hs=
-github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 h1:VNqngBF40hVlDloBruUehVYC3ArSgIyScOAyMRqBxRg=
-github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1/go.mod h1:RBRO7fro65R6tjKzYgLAFo0t1QEXY1Dp+i/bvpRiqiQ=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.0 h1:VD1gqscl4nYs1YxVuSdemTrSgTKrwOWDK0FVFMqm+Cg=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.0/go.mod h1:4EgsQoS4TOhJizV+JTFg40qx1Ofh3XmXEQNBpgvNT40=
 github.com/hashicorp/go-immutable-radix/v2 v2.1.0 h1:CUW5RYIcysz+D3B+l1mDeXrQ7fUvGGCwJfdASSzbrfo=
 github.com/hashicorp/go-immutable-radix/v2 v2.1.0/go.mod h1:hgdqLXA4f6NIjRVisM1TJ9aOJVNRqKZj+xDGF6m7PBw=
 github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
@@ -299,8 +299,9 @@ github.com/matoous/godox v0.0.0-20230222163458-006bad1f9d26 h1:gWg6ZQ4JhDfJPqlo2
 github.com/matoous/godox v0.0.0-20230222163458-006bad1f9d26/go.mod h1:1BELzlh859Sh1c6+90blK8lbYy0kwQf1bYlBhBysy1s=
 github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE=
 github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
-github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
 github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
+github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
+github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
 github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
 github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
 github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
@@ -360,8 +361,8 @@ github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+
 github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
 github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
 github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
-github.com/prometheus/common v0.61.0 h1:3gv/GThfX0cV2lpO7gkTUwZru38mxevy90Bj8YFSRQQ=
-github.com/prometheus/common v0.61.0/go.mod h1:zr29OCN/2BsJRaFwG8QOBr41D6kkchKbpeNH7pAjb/s=
+github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
+github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
 github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
 github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
 github.com/quasilyte/go-ruleguard v0.4.3-0.20240823090925-0fe6f58b47b1 h1:+Wl/0aFp0hpuHM3H//KMft64WQ1yX9LdJY64Qm/gFCo=
@@ -699,20 +700,20 @@ golang.org/x/tools v0.11.0/go.mod h1:anzJrxPjNtfgiYQYirP2CPGzGLxrH2u2QBhn6Bf3qY8
 golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
 golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg=
 golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc=
-golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8=
-golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw=
+golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE=
+golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-google.golang.org/genproto/googleapis/api v0.0.0-20250102185135-69823020774d h1:H8tOf8XM88HvKqLTxe755haY6r1fqqzLbEnfrmLXlSA=
-google.golang.org/genproto/googleapis/api v0.0.0-20250102185135-69823020774d/go.mod h1:2v7Z7gP2ZUOGsaFyxATQSRoBnKygqVq2Cwnvom7QiqY=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d h1:xJJRGY7TJcvIlpSrN3K6LAWgNFUILlO+OMAqtg9aqnw=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d/go.mod h1:3ENsm/5D1mzDyhpzeRi1NR784I0BcofWBoSc5QqqMK4=
-google.golang.org/grpc v1.69.4 h1:MF5TftSMkd8GLw/m0KM6V8CMOCY6NZ1NQDPGFgbTt4A=
-google.golang.org/grpc v1.69.4/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4=
-google.golang.org/protobuf v1.36.2 h1:R8FeyR1/eLmkutZOM5CWghmo5itiG9z0ktFlTVLuTmU=
-google.golang.org/protobuf v1.36.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
+google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f h1:gap6+3Gk41EItBuyi4XX/bp4oqJ3UwuIMl25yGinuAA=
+google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:Ic02D47M+zbarjYYUlK57y316f2MoN0gjAwI3f2S95o=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f h1:OxYkA3wjPsZyBylwymxSHa7ViiW1Sml4ToBrncvFehI=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:+2Yz8+CLJbIfL9z73EW45avw8Lmge3xVElCP9zEKi50=
+google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ=
+google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw=
+google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM=
+google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
diff --git a/tools/testgrid-analysis/go.mod b/tools/testgrid-analysis/go.mod
index 9d8f280c0..5d8a30bc2 100644
--- a/tools/testgrid-analysis/go.mod
+++ b/tools/testgrid-analysis/go.mod
@@ -8,18 +8,18 @@ require (
 	github.com/GoogleCloudPlatform/testgrid v0.0.173
 	github.com/google/go-github/v60 v60.0.0
 	github.com/spf13/cobra v1.8.1
-	google.golang.org/protobuf v1.36.2
+	google.golang.org/protobuf v1.36.4
 )
 
 require (
 	github.com/google/go-querystring v1.1.0 // indirect
 	github.com/inconshreveable/mousetrap v1.1.0 // indirect
 	github.com/spf13/pflag v1.0.5 // indirect
-	go.opentelemetry.io/otel v1.33.0 // indirect
-	go.opentelemetry.io/otel/sdk v1.33.0 // indirect
+	go.opentelemetry.io/otel v1.34.0 // indirect
+	go.opentelemetry.io/otel/sdk v1.34.0 // indirect
 	golang.org/x/net v0.34.0 // indirect
 	golang.org/x/sys v0.29.0 // indirect
 	golang.org/x/text v0.21.0 // indirect
-	google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d // indirect
-	google.golang.org/grpc v1.69.4 // indirect
+	google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect
+	google.golang.org/grpc v1.70.0 // indirect
 )
diff --git a/tools/testgrid-analysis/go.sum b/tools/testgrid-analysis/go.sum
index 3b0f93bd9..bc0af0a64 100644
--- a/tools/testgrid-analysis/go.sum
+++ b/tools/testgrid-analysis/go.sum
@@ -1179,16 +1179,16 @@ go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
 go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
 go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
 go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
-go.opentelemetry.io/otel v1.33.0 h1:/FerN9bax5LoK51X/sI0SVYrjSE0/yUL7DpxW4K3FWw=
-go.opentelemetry.io/otel v1.33.0/go.mod h1:SUUkR6csvUQl+yjReHu5uM3EtVV7MBm5FHKRlNx4I8I=
-go.opentelemetry.io/otel/metric v1.33.0 h1:r+JOocAyeRVXD8lZpjdQjzMadVZp2M4WmQ+5WtEnklQ=
-go.opentelemetry.io/otel/metric v1.33.0/go.mod h1:L9+Fyctbp6HFTddIxClbQkjtubW6O9QS3Ann/M82u6M=
-go.opentelemetry.io/otel/sdk v1.33.0 h1:iax7M131HuAm9QkZotNHEfstof92xM+N8sr3uHXc2IM=
-go.opentelemetry.io/otel/sdk v1.33.0/go.mod h1:A1Q5oi7/9XaMlIWzPSxLRWOI8nG3FnzHJNbiENQuihM=
-go.opentelemetry.io/otel/sdk/metric v1.31.0 h1:i9hxxLJF/9kkvfHppyLL55aW7iIJz4JjxTeYusH7zMc=
-go.opentelemetry.io/otel/sdk/metric v1.31.0/go.mod h1:CRInTMVvNhUKgSAMbKyTMxqOBC0zgyxzW55lZzX43Y8=
-go.opentelemetry.io/otel/trace v1.33.0 h1:cCJuF7LRjUFso9LPnEAHJDB2pqzp+hbO8eu1qqW2d/s=
-go.opentelemetry.io/otel/trace v1.33.0/go.mod h1:uIcdVUZMpTAmz0tI1z04GoVSezK37CbGV4fr1f2nBck=
+go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY=
+go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI=
+go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ=
+go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE=
+go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A=
+go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU=
+go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU=
+go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ=
+go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k=
+go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE=
 go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
 go.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U=
 go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U=
@@ -1840,8 +1840,8 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20230706204954-ccb25ca9f130/go.
 google.golang.org/genproto/googleapis/rpc v0.0.0-20230720185612-659f7aaaa771/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM=
 google.golang.org/genproto/googleapis/rpc v0.0.0-20230731190214-cbb8c96f2d6d/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM=
 google.golang.org/genproto/googleapis/rpc v0.0.0-20230731193218-e0aa005b6bdf/go.mod h1:zBEcrKX2ZOcEkHWxBPAIvYUWOKKMIhYcmNiUIu2ji3I=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d h1:xJJRGY7TJcvIlpSrN3K6LAWgNFUILlO+OMAqtg9aqnw=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d/go.mod h1:3ENsm/5D1mzDyhpzeRi1NR784I0BcofWBoSc5QqqMK4=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f h1:OxYkA3wjPsZyBylwymxSHa7ViiW1Sml4ToBrncvFehI=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:+2Yz8+CLJbIfL9z73EW45avw8Lmge3xVElCP9zEKi50=
 google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
 google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
@@ -1887,8 +1887,8 @@ google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGO
 google.golang.org/grpc v1.56.1/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s=
 google.golang.org/grpc v1.56.2/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s=
 google.golang.org/grpc v1.57.0/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo=
-google.golang.org/grpc v1.69.4 h1:MF5TftSMkd8GLw/m0KM6V8CMOCY6NZ1NQDPGFgbTt4A=
-google.golang.org/grpc v1.69.4/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4=
+google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ=
+google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw=
 google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
 google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
 google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
@@ -1908,8 +1908,8 @@ google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw
 google.golang.org/protobuf v1.29.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
 google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
 google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
-google.golang.org/protobuf v1.36.2 h1:R8FeyR1/eLmkutZOM5CWghmo5itiG9z0ktFlTVLuTmU=
-google.golang.org/protobuf v1.36.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
+google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM=
+google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
 gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=



Test patch:
diff --git a/server/auth/jwt_test.go b/server/auth/jwt_test.go
index 0ef513b21..f3903d636 100644
--- a/server/auth/jwt_test.go
+++ b/server/auth/jwt_test.go
@@ -22,7 +22,7 @@ import (
 	"testing"
 	"time"
 
-	"github.com/golang-jwt/jwt/v4"
+	"github.com/golang-jwt/jwt/v5"
 	"github.com/stretchr/testify/require"
 	"go.uber.org/zap"
 )
diff --git a/server/embed/config_test.go b/server/embed/config_test.go
index 9a24e5eb9..757769b87 100644
--- a/server/embed/config_test.go
+++ b/server/embed/config_test.go
@@ -17,6 +17,7 @@ package embed
 import (
 	"crypto/tls"
 	"errors"
+	"flag"
 	"fmt"
 	"net"
 	"net/url"
@@ -25,11 +26,14 @@ import (
 	"testing"
 	"time"
 
+	"github.com/google/go-cmp/cmp"
+	"github.com/google/go-cmp/cmp/cmpopts"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 	"sigs.k8s.io/yaml"
 
 	"go.etcd.io/etcd/client/pkg/v3/srv"
+	"go.etcd.io/etcd/client/pkg/v3/tlsutil"
 	"go.etcd.io/etcd/client/pkg/v3/transport"
 	"go.etcd.io/etcd/client/pkg/v3/types"
 	"go.etcd.io/etcd/pkg/v3/featuregate"
@@ -1041,3 +1045,27 @@ func TestSetFeatureGatesFromExperimentalFlags(t *testing.T) {
 		})
 	}
 }
+
+func TestMatchNewConfigAddFlags(t *testing.T) {
+	cfg := NewConfig()
+	fs := flag.NewFlagSet("etcd", flag.ContinueOnError)
+	cfg.AddFlags(fs)
+	require.NoError(t, fs.Parse(nil))
+
+	newConfig := NewConfig()
+	// TODO: Remove the following assigments when both match.
+	newConfig.SelfSignedCertValidity = 1
+	newConfig.TlsMinVersion = string(tlsutil.TLSVersion12)
+	newConfig.DiscoveryCfg.Secure.InsecureTransport = true
+	newConfig.AutoCompactionRetention = "0"
+	newConfig.ExperimentalDistributedTracingAddress = "localhost:4317"
+	newConfig.ExperimentalDistributedTracingServiceName = "etcd"
+	newConfig.LogFormat = "json"
+	newConfig.ExperimentalTxnModeWriteWithSharedBuffer = true
+	// TODO: Reduce number of unexported fields set in config
+	if diff := cmp.Diff(newConfig, cfg, cmpopts.IgnoreUnexported(transport.TLSInfo{}, Config{}), cmp.Comparer(func(a, b featuregate.FeatureGate) bool {
+		return a.String() == b.String()
+	})); diff != "" {
+		t.Errorf("Diff: %s", diff)
+	}
+}
diff --git a/server/etcdmain/config_test.go b/server/etcdmain/config_test.go
index 634921b66..c8380d940 100644
--- a/server/etcdmain/config_test.go
+++ b/server/etcdmain/config_test.go
@@ -26,6 +26,7 @@ import (
 	"time"
 
 	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
 	"sigs.k8s.io/yaml"
 
 	"go.etcd.io/etcd/pkg/v3/featuregate"
@@ -520,18 +521,14 @@ func TestCompactHashCheckTimeFlagMigration(t *testing.T) {
 			if tc.compactHashCheckTime != "" {
 				cmdLineArgs = append(cmdLineArgs, fmt.Sprintf("--compact-hash-check-time=%s", tc.compactHashCheckTime))
 				compactHashCheckTime, err := time.ParseDuration(tc.compactHashCheckTime)
-				if err != nil {
-					t.Fatal(err)
-				}
+				require.NoError(t, err)
 				yc.CompactHashCheckTime = compactHashCheckTime
 			}
 
 			if tc.experimentalCompactHashCheckTime != "" {
 				cmdLineArgs = append(cmdLineArgs, fmt.Sprintf("--experimental-compact-hash-check-time=%s", tc.experimentalCompactHashCheckTime))
 				experimentalCompactHashCheckTime, err := time.ParseDuration(tc.experimentalCompactHashCheckTime)
-				if err != nil {
-					t.Fatal(err)
-				}
+				require.NoError(t, err)
 				yc.ExperimentalCompactHashCheckTime = experimentalCompactHashCheckTime
 			}
 
@@ -547,12 +544,8 @@ func TestCompactHashCheckTimeFlagMigration(t *testing.T) {
 				t.Fatal("error parsing config")
 			}
 
-			if cfgFromCmdLine.ec.CompactHashCheckTime != tc.expectedCompactHashCheckTime {
-				t.Errorf("expected CompactHashCheckTime=%v, got %v", tc.expectedCompactHashCheckTime, cfgFromCmdLine.ec.CompactHashCheckTime)
-			}
-			if cfgFromFile.ec.CompactHashCheckTime != tc.expectedCompactHashCheckTime {
-				t.Errorf("expected CompactHashCheckTime=%v, got %v", tc.expectedCompactHashCheckTime, cfgFromFile.ec.CompactHashCheckTime)
-			}
+			require.Equal(t, tc.expectedCompactHashCheckTime, cfgFromCmdLine.ec.CompactHashCheckTime)
+			require.Equal(t, tc.expectedCompactHashCheckTime, cfgFromFile.ec.CompactHashCheckTime)
 		})
 	}
 }
@@ -596,18 +589,14 @@ func TestCorruptCheckTimeFlagMigration(t *testing.T) {
 			if tc.corruptCheckTime != "" {
 				cmdLineArgs = append(cmdLineArgs, fmt.Sprintf("--corrupt-check-time=%s", tc.corruptCheckTime))
 				corruptCheckTime, err := time.ParseDuration(tc.corruptCheckTime)
-				if err != nil {
-					t.Fatal(err)
-				}
+				require.NoError(t, err)
 				yc.CorruptCheckTime = corruptCheckTime
 			}
 
 			if tc.experimentalCorruptCheckTime != "" {
 				cmdLineArgs = append(cmdLineArgs, fmt.Sprintf("--experimental-corrupt-check-time=%s", tc.experimentalCorruptCheckTime))
 				experimentalCorruptCheckTime, err := time.ParseDuration(tc.experimentalCorruptCheckTime)
-				if err != nil {
-					t.Fatal(err)
-				}
+				require.NoError(t, err)
 				yc.ExperimentalCorruptCheckTime = experimentalCorruptCheckTime
 			}
 
@@ -623,12 +612,8 @@ func TestCorruptCheckTimeFlagMigration(t *testing.T) {
 				t.Fatal("error parsing config")
 			}
 
-			if cfgFromCmdLine.ec.CorruptCheckTime != tc.expectedCorruptCheckTime {
-				t.Errorf("expected CorruptCheckTime=%v, got %v", tc.expectedCorruptCheckTime, cfgFromCmdLine.ec.CorruptCheckTime)
-			}
-			if cfgFromFile.ec.CorruptCheckTime != tc.expectedCorruptCheckTime {
-				t.Errorf("expected CorruptCheckTime=%v, got %v", tc.expectedCorruptCheckTime, cfgFromFile.ec.CorruptCheckTime)
-			}
+			require.Equal(t, tc.expectedCorruptCheckTime, cfgFromCmdLine.ec.CorruptCheckTime)
+			require.Equal(t, tc.expectedCorruptCheckTime, cfgFromFile.ec.CorruptCheckTime)
 		})
 	}
 }
@@ -691,12 +676,272 @@ func TestCompactionBatchLimitFlagMigration(t *testing.T) {
 				t.Fatal("error parsing config")
 			}
 
-			if cfgFromCmdLine.ec.CompactionBatchLimit != tc.expectedCompactionBatchLimit {
-				t.Errorf("expected CorruptCheckTime=%v, got %v", tc.expectedCompactionBatchLimit, cfgFromCmdLine.ec.CompactionBatchLimit)
+			require.Equal(t, tc.expectedCompactionBatchLimit, cfgFromCmdLine.ec.CompactionBatchLimit)
+			require.Equal(t, tc.expectedCompactionBatchLimit, cfgFromFile.ec.CompactionBatchLimit)
+		})
+	}
+}
+
+// TestWatchProgressNotifyInterval tests the migration from
+// --experimental-watch-progress-notify-interval to --watch-progress-notify-interval
+// TODO: delete in v3.7
+func TestWatchProgressNotifyInterval(t *testing.T) {
+	testCases := []struct {
+		name                                    string
+		watchProgressNotifyInterval             string
+		experimentalWatchProgressNotifyInterval string
+		expectErr                               bool
+		expectedWatchProgressNotifyInterval     time.Duration
+	}{
+		{
+			name:                                    "cannot set both experimental flag and non experimental flag",
+			watchProgressNotifyInterval:             "2m",
+			experimentalWatchProgressNotifyInterval: "3m",
+			expectErr:                               true,
+		},
+		{
+			name:                                    "can set experimental flag",
+			experimentalWatchProgressNotifyInterval: "3m",
+			expectedWatchProgressNotifyInterval:     3 * time.Minute,
+		},
+		{
+			name:                                "can set non experimental flag",
+			watchProgressNotifyInterval:         "2m",
+			expectedWatchProgressNotifyInterval: 2 * time.Minute,
+		},
+	}
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			cmdLineArgs := []string{}
+			yc := struct {
+				ExperimentalWatchProgressNotifyInterval time.Duration `json:"experimental-watch-progress-notify-interval,omitempty"`
+				WatchProgressNotifyInterval             time.Duration `json:"watch-progress-notify-interval,omitempty"`
+			}{}
+
+			if tc.watchProgressNotifyInterval != "" {
+				cmdLineArgs = append(cmdLineArgs, fmt.Sprintf("--watch-progress-notify-interval=%s", tc.watchProgressNotifyInterval))
+				watchProgressNotifyInterval, err := time.ParseDuration(tc.watchProgressNotifyInterval)
+				require.NoError(t, err)
+				yc.WatchProgressNotifyInterval = watchProgressNotifyInterval
+			}
+
+			if tc.experimentalWatchProgressNotifyInterval != "" {
+				cmdLineArgs = append(cmdLineArgs, fmt.Sprintf("--experimental-watch-progress-notify-interval=%s", tc.experimentalWatchProgressNotifyInterval))
+				experimentalWatchProgressNotifyInterval, err := time.ParseDuration(tc.experimentalWatchProgressNotifyInterval)
+				require.NoError(t, err)
+				yc.ExperimentalWatchProgressNotifyInterval = experimentalWatchProgressNotifyInterval
+			}
+
+			cfgFromCmdLine, errFromCmdLine, cfgFromFile, errFromFile := generateCfgsFromFileAndCmdLine(t, yc, cmdLineArgs)
+
+			if tc.expectErr {
+				if errFromCmdLine == nil || errFromFile == nil {
+					t.Fatal("expect parse error")
+				}
+				return
+			}
+			if errFromCmdLine != nil || errFromFile != nil {
+				t.Fatal("error parsing config")
+			}
+
+			require.Equal(t, tc.expectedWatchProgressNotifyInterval, cfgFromCmdLine.ec.WatchProgressNotifyInterval)
+			require.Equal(t, tc.expectedWatchProgressNotifyInterval, cfgFromFile.ec.WatchProgressNotifyInterval)
+		})
+	}
+}
+
+// TestWarningApplyDuration tests the migration from
+// --experimental-warning-apply-duration to --warning-apply-duration
+// TODO: delete in v3.7
+func TestWarningApplyDuration(t *testing.T) {
+	testCases := []struct {
+		name                             string
+		warningApplyDuration             string
+		experimentalWarningApplyDuration string
+		expectErr                        bool
+		expectedWarningApplyDuration     time.Duration
+	}{
+		{
+			name:                             "cannot set both experimental flag and non experimental flag",
+			warningApplyDuration:             "2m",
+			experimentalWarningApplyDuration: "3m",
+			expectErr:                        true,
+		},
+		{
+			name:                             "can set experimental flag",
+			experimentalWarningApplyDuration: "3m",
+			expectedWarningApplyDuration:     3 * time.Minute,
+		},
+		{
+			name:                         "can set non experimental flag",
+			warningApplyDuration:         "2m",
+			expectedWarningApplyDuration: 2 * time.Minute,
+		},
+	}
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			cmdLineArgs := []string{}
+			yc := struct {
+				ExperimentalWarningApplyDuration time.Duration `json:"experimental-warning-apply-duration,omitempty"`
+				WarningApplyDuration             time.Duration `json:"warning-apply-duration,omitempty"`
+			}{}
+
+			if tc.warningApplyDuration != "" {
+				cmdLineArgs = append(cmdLineArgs, fmt.Sprintf("--warning-apply-duration=%s", tc.warningApplyDuration))
+				warningApplyDuration, err := time.ParseDuration(tc.warningApplyDuration)
+				require.NoError(t, err)
+				yc.WarningApplyDuration = warningApplyDuration
+			}
+
+			if tc.experimentalWarningApplyDuration != "" {
+				cmdLineArgs = append(cmdLineArgs, fmt.Sprintf("--experimental-warning-apply-duration=%s", tc.experimentalWarningApplyDuration))
+				experimentalWarningApplyDuration, err := time.ParseDuration(tc.experimentalWarningApplyDuration)
+				require.NoError(t, err)
+				yc.ExperimentalWarningApplyDuration = experimentalWarningApplyDuration
+			}
+
+			cfgFromCmdLine, errFromCmdLine, cfgFromFile, errFromFile := generateCfgsFromFileAndCmdLine(t, yc, cmdLineArgs)
+
+			if tc.expectErr {
+				if errFromCmdLine == nil || errFromFile == nil {
+					t.Fatal("expect parse error")
+				}
+				return
+			}
+			if errFromCmdLine != nil || errFromFile != nil {
+				t.Fatal("error parsing config")
+			}
+
+			require.Equal(t, tc.expectedWarningApplyDuration, cfgFromCmdLine.ec.WarningApplyDuration)
+			require.Equal(t, tc.expectedWarningApplyDuration, cfgFromFile.ec.WarningApplyDuration)
+		})
+	}
+}
+
+// TestBootstrapDefragThresholdMegabytesFlagMigration tests the migration from
+// --experimental-bootstrap-defrag-threshold-megabytes to --bootstrap-defrag-threshold-megabytes
+// TODO: delete in v3.7
+func TestBootstrapDefragThresholdMegabytesFlagMigration(t *testing.T) {
+	testCases := []struct {
+		name                                          string
+		bootstrapDefragThresholdMegabytes             uint
+		experimentalBootstrapDefragThresholdMegabytes uint
+		expectErr                                     bool
+		expectedBootstrapDefragThresholdMegabytes     uint
+	}{
+		{
+			name:                              "cannot set both experimental flag and non experimental flag",
+			bootstrapDefragThresholdMegabytes: 100,
+			experimentalBootstrapDefragThresholdMegabytes: 200,
+			expectErr: true,
+		},
+		{
+			name: "can set experimental flag",
+			experimentalBootstrapDefragThresholdMegabytes: 200,
+			expectedBootstrapDefragThresholdMegabytes:     200,
+		},
+		{
+			name:                              "can set non experimental flag",
+			bootstrapDefragThresholdMegabytes: 100,
+			expectedBootstrapDefragThresholdMegabytes: 100,
+		},
+	}
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			cmdLineArgs := []string{}
+			yc := struct {
+				ExperimentalBootstrapDefragThresholdMegabytes uint `json:"experimental-bootstrap-defrag-threshold-megabytes,omitempty"`
+				BootstrapDefragThresholdMegabytes             uint `json:"bootstrap-defrag-threshold-megabytes,omitempty"`
+			}{}
+
+			if tc.bootstrapDefragThresholdMegabytes != 0 {
+				cmdLineArgs = append(cmdLineArgs, fmt.Sprintf("--bootstrap-defrag-threshold-megabytes=%d", tc.bootstrapDefragThresholdMegabytes))
+				yc.BootstrapDefragThresholdMegabytes = tc.bootstrapDefragThresholdMegabytes
+			}
+
+			if tc.experimentalBootstrapDefragThresholdMegabytes != 0 {
+				cmdLineArgs = append(cmdLineArgs, fmt.Sprintf("--experimental-bootstrap-defrag-threshold-megabytes=%d", tc.experimentalBootstrapDefragThresholdMegabytes))
+				yc.ExperimentalBootstrapDefragThresholdMegabytes = tc.experimentalBootstrapDefragThresholdMegabytes
+			}
+
+			cfgFromCmdLine, errFromCmdLine, cfgFromFile, errFromFile := generateCfgsFromFileAndCmdLine(t, yc, cmdLineArgs)
+
+			if tc.expectErr {
+				if errFromCmdLine == nil || errFromFile == nil {
+					t.Fatal("expect parse error")
+				}
+				return
+			}
+			if errFromCmdLine != nil || errFromFile != nil {
+				t.Fatal("error parsing config")
+			}
+
+			require.Equal(t, tc.expectedBootstrapDefragThresholdMegabytes, cfgFromCmdLine.ec.BootstrapDefragThresholdMegabytes)
+			require.Equal(t, tc.expectedBootstrapDefragThresholdMegabytes, cfgFromFile.ec.BootstrapDefragThresholdMegabytes)
+		})
+	}
+}
+
+// TestMaxLearnersFlagMigration tests the migration from
+// --experimental-max-learners to --max-learners
+// TODO: delete in v3.7
+func TestMaxLearnersFlagMigration(t *testing.T) {
+	testCases := []struct {
+		name                    string
+		maxLearners             int
+		experimentalMaxLearners int
+		expectErr               bool
+		expectedMaxLearners     int
+	}{
+		{
+			name:                    "cannot set both experimental flag and non experimental flag",
+			maxLearners:             1,
+			experimentalMaxLearners: 2,
+			expectErr:               true,
+		},
+		{
+			name:                    "can set experimental flag",
+			experimentalMaxLearners: 2,
+			expectedMaxLearners:     2,
+		},
+		{
+			name:                "can set non experimental flag",
+			maxLearners:         1,
+			expectedMaxLearners: 1,
+		},
+	}
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			cmdLineArgs := []string{}
+			yc := struct {
+				ExperimentalMaxLearners int `json:"experimental-max-learners,omitempty"`
+				MaxLearners             int `json:"max-learners,omitempty"`
+			}{}
+
+			if tc.maxLearners != 0 {
+				cmdLineArgs = append(cmdLineArgs, fmt.Sprintf("--max-learners=%d", tc.maxLearners))
+				yc.MaxLearners = tc.maxLearners
+			}
+
+			if tc.experimentalMaxLearners != 0 {
+				cmdLineArgs = append(cmdLineArgs, fmt.Sprintf("--experimental-max-learners=%d", tc.experimentalMaxLearners))
+				yc.ExperimentalMaxLearners = tc.experimentalMaxLearners
+			}
+
+			cfgFromCmdLine, errFromCmdLine, cfgFromFile, errFromFile := generateCfgsFromFileAndCmdLine(t, yc, cmdLineArgs)
+
+			if tc.expectErr {
+				if errFromCmdLine == nil || errFromFile == nil {
+					t.Fatal("expect parse error")
+				}
+				return
 			}
-			if cfgFromFile.ec.CompactionBatchLimit != tc.expectedCompactionBatchLimit {
-				t.Errorf("expected CorruptCheckTime=%v, got %v", tc.expectedCompactionBatchLimit, cfgFromFile.ec.CompactionBatchLimit)
+			if errFromCmdLine != nil || errFromFile != nil {
+				t.Fatal("error parsing config")
 			}
+
+			require.Equal(t, tc.expectedMaxLearners, cfgFromCmdLine.ec.MaxLearners)
+			require.Equal(t, tc.expectedMaxLearners, cfgFromFile.ec.MaxLearners)
 		})
 	}
 }
@@ -704,9 +949,7 @@ func TestCompactionBatchLimitFlagMigration(t *testing.T) {
 // TODO delete in v3.7
 func generateCfgsFromFileAndCmdLine(t *testing.T, yc any, cmdLineArgs []string) (*config, error, *config, error) {
 	b, err := yaml.Marshal(&yc)
-	if err != nil {
-		t.Fatal(err)
-	}
+	require.NoError(t, err)
 
 	tmpfile := mustCreateCfgFile(t, b)
 	defer os.Remove(tmpfile.Name())
@@ -807,13 +1050,17 @@ func validateClusteringFlags(t *testing.T, cfg *config) {
 func TestConfigFileDeprecatedOptions(t *testing.T) {
 	// Define a minimal config struct with only the fields we need
 	type configFileYAML struct {
-		SnapshotCount                           uint64        `json:"snapshot-count,omitempty"`
-		MaxSnapFiles                            uint          `json:"max-snapshots,omitempty"`
-		ExperimentalCompactHashCheckEnabled     bool          `json:"experimental-compact-hash-check-enabled,omitempty"`
-		ExperimentalCompactHashCheckTime        time.Duration `json:"experimental-compact-hash-check-time,omitempty"`
-		ExperimentalWarningUnaryRequestDuration time.Duration `json:"experimental-warning-unary-request-duration,omitempty"`
-		ExperimentalCorruptCheckTime            time.Duration `json:"experimental-corrupt-check-time,omitempty"`
-		ExperimentalCompactionBatchLimit        int           `json:"experimental-compaction-batch-limit,omitempty"`
+		SnapshotCount                                 uint64        `json:"snapshot-count,omitempty"`
+		MaxSnapFiles                                  uint          `json:"max-snapshots,omitempty"`
+		ExperimentalCompactHashCheckEnabled           bool          `json:"experimental-compact-hash-check-enabled,omitempty"`
+		ExperimentalCompactHashCheckTime              time.Duration `json:"experimental-compact-hash-check-time,omitempty"`
+		ExperimentalWarningUnaryRequestDuration       time.Duration `json:"experimental-warning-unary-request-duration,omitempty"`
+		ExperimentalCorruptCheckTime                  time.Duration `json:"experimental-corrupt-check-time,omitempty"`
+		ExperimentalCompactionBatchLimit              int           `json:"experimental-compaction-batch-limit,omitempty"`
+		ExperimentalWatchProgressNotifyInterval       time.Duration `json:"experimental-watch-progress-notify-interval,omitempty"`
+		ExperimentalWarningApplyDuration              time.Duration `json:"experimental-warning-apply-duration,omitempty"`
+		ExperimentalBootstrapDefragThresholdMegabytes uint          `json:"experimental-bootstrap-defrag-threshold-megabytes,omitempty"`
+		ExperimentalMaxLearners                       int           `json:"experimental-max-learners,omitempty"`
 	}
 
 	testCases := []struct {
@@ -829,17 +1076,25 @@ func TestConfigFileDeprecatedOptions(t *testing.T) {
 		{
 			name: "deprecated experimental options",
 			configFileYAML: configFileYAML{
-				ExperimentalCompactHashCheckEnabled:     true,
-				ExperimentalCompactHashCheckTime:        2 * time.Minute,
-				ExperimentalWarningUnaryRequestDuration: time.Second,
-				ExperimentalCorruptCheckTime:            time.Minute,
-				ExperimentalCompactionBatchLimit:        1,
+				ExperimentalCompactHashCheckEnabled:           true,
+				ExperimentalCompactHashCheckTime:              2 * time.Minute,
+				ExperimentalWarningUnaryRequestDuration:       time.Second,
+				ExperimentalCorruptCheckTime:                  time.Minute,
+				ExperimentalCompactionBatchLimit:              1,
+				ExperimentalWatchProgressNotifyInterval:       3 * time.Minute,
+				ExperimentalWarningApplyDuration:              3 * time.Minute,
+				ExperimentalBootstrapDefragThresholdMegabytes: 100,
+				ExperimentalMaxLearners:                       1,
 			},
 			expectedFlags: map[string]struct{}{
-				"experimental-compact-hash-check-enabled": {},
-				"experimental-compact-hash-check-time":    {},
-				"experimental-corrupt-check-time":         {},
-				"experimental-compaction-batch-limit":     {},
+				"experimental-compact-hash-check-enabled":           {},
+				"experimental-compact-hash-check-time":              {},
+				"experimental-corrupt-check-time":                   {},
+				"experimental-compaction-batch-limit":               {},
+				"experimental-watch-progress-notify-interval":       {},
+				"experimental-warning-apply-duration":               {},
+				"experimental-bootstrap-defrag-threshold-megabytes": {},
+				"experimental-max-learners":                         {},
 			},
 		},
 		{
diff --git a/server/etcdserver/api/etcdhttp/health_test.go b/server/etcdserver/api/etcdhttp/health_test.go
index a5f640619..06226e136 100644
--- a/server/etcdserver/api/etcdhttp/health_test.go
+++ b/server/etcdserver/api/etcdhttp/health_test.go
@@ -43,6 +43,7 @@ type fakeHealthServer struct {
 	linearizableReadError error
 	missingLeader         bool
 	authStore             auth.AuthStore
+	isLearner             bool
 }
 
 func (s *fakeHealthServer) Range(_ context.Context, req *pb.RangeRequest) (*pb.RangeResponse, error) {
@@ -52,6 +53,10 @@ func (s *fakeHealthServer) Range(_ context.Context, req *pb.RangeRequest) (*pb.R
 	return nil, s.linearizableReadError
 }
 
+func (s *fakeHealthServer) IsLearner() bool {
+	return s.isLearner
+}
+
 func (s *fakeHealthServer) Config() config.ServerConfig {
 	return config.ServerConfig{}
 }
@@ -77,6 +82,7 @@ type healthTestCase struct {
 	alarms        []*pb.AlarmMember
 	apiError      error
 	missingLeader bool
+	isLearner     bool
 }
 
 func TestHealthHandler(t *testing.T) {
@@ -181,6 +187,11 @@ func TestHTTPSubPath(t *testing.T) {
 			expectStatusCode: http.StatusServiceUnavailable,
 			notInResult:      []string{"data_corruption"},
 		},
+		{
+			name:             "/readyz/learner ok",
+			healthCheckURL:   "/readyz/non_learner",
+			expectStatusCode: http.StatusOK,
+		},
 		{
 			name:             "/readyz/non_exist 404",
 			healthCheckURL:   "/readyz/non_exist",
@@ -344,6 +355,42 @@ func TestLinearizableReadCheck(t *testing.T) {
 	}
 }
 
+func TestLearnerReadyCheck(t *testing.T) {
+	be, _ := betesting.NewDefaultTmpBackend(t)
+	defer betesting.Close(t, be)
+	tests := []healthTestCase{
+		{
+			name:             "readyz normal",
+			healthCheckURL:   "/readyz",
+			expectStatusCode: http.StatusOK,
+			isLearner:        false,
+		},
+		{
+			name:             "not ready because member is learner",
+			healthCheckURL:   "/readyz",
+			expectStatusCode: http.StatusServiceUnavailable,
+			isLearner:        true,
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			mux := http.NewServeMux()
+			logger := zaptest.NewLogger(t)
+			s := &fakeHealthServer{
+				linearizableReadError: tt.apiError,
+				authStore:             auth.NewAuthStore(logger, schema.NewAuthBackend(logger, be), nil, 0),
+			}
+			s.isLearner = tt.isLearner
+			HandleHealth(logger, mux, s)
+			ts := httptest.NewServer(mux)
+			defer ts.Close()
+			checkHTTPResponse(t, ts, tt.healthCheckURL, tt.expectStatusCode, tt.inResult, tt.notInResult)
+			checkMetrics(t, tt.healthCheckURL, "linearizable_read", tt.expectStatusCode)
+		})
+	}
+}
+
 func checkHTTPResponse(t *testing.T, ts *httptest.Server, url string, expectStatusCode int, inResult []string, notInResult []string) {
 	res, err := ts.Client().Do(&http.Request{Method: http.MethodGet, URL: testutil.MustNewURL(t, ts.URL+url)})
 	if err != nil {
diff --git a/server/etcdserver/bootstrap_test.go b/server/etcdserver/bootstrap_test.go
index a692a6856..6fd4bd8a2 100644
--- a/server/etcdserver/bootstrap_test.go
+++ b/server/etcdserver/bootstrap_test.go
@@ -94,10 +94,10 @@ func TestBootstrapExistingClusterNoWALMaxLearner(t *testing.T) {
 				t.Fatalf("unexpected error: %v", err)
 			}
 			cfg := config.ServerConfig{
-				Name:                    "node0",
-				InitialPeerURLsMap:      cluster,
-				Logger:                  zaptest.NewLogger(t),
-				ExperimentalMaxLearners: tt.maxLearner,
+				Name:               "node0",
+				InitialPeerURLsMap: cluster,
+				Logger:             zaptest.NewLogger(t),
+				MaxLearners:        tt.maxLearner,
 			}
 			_, err = bootstrapExistingClusterNoWAL(cfg, mockBootstrapRoundTrip(tt.members))
 			hasError := err != nil
diff --git a/server/storage/mvcc/key_index_test.go b/server/storage/mvcc/key_index_test.go
index f86e027e5..643ba11da 100644
--- a/server/storage/mvcc/key_index_test.go
+++ b/server/storage/mvcc/key_index_test.go
@@ -20,10 +20,51 @@ import (
 	"testing"
 
 	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
 	"go.uber.org/zap"
 	"go.uber.org/zap/zaptest"
 )
 
+func TestRestoreTombstone(t *testing.T) {
+	lg := zaptest.NewLogger(t)
+
+	// restore from tombstone
+	//
+	// key: "foo"
+	// modified: 16
+	// "created": 16
+	// generations:
+	//    {empty}
+	//    {{16, 0}(t)[0]}
+	//
+	ki := &keyIndex{key: []byte("foo")}
+	ki.restoreTombstone(lg, 16, 0)
+
+	// get should return not found
+	for retAt := 16; retAt <= 20; retAt++ {
+		_, _, _, err := ki.get(lg, int64(retAt))
+		require.ErrorIs(t, err, ErrRevisionNotFound)
+	}
+
+	// doCompact should keep that tombstone
+	availables := map[Revision]struct{}{}
+	ki.doCompact(16, availables)
+	require.Len(t, availables, 1)
+	_, ok := availables[Revision{Main: 16}]
+	require.True(t, ok)
+
+	// should be able to put new revisions
+	ki.put(lg, 17, 0)
+	ki.put(lg, 18, 0)
+	revs := ki.since(lg, 16)
+	require.Equal(t, []Revision{{16, 0}, {17, 0}, {18, 0}}, revs)
+
+	// compaction should remove restored tombstone
+	ki.compact(lg, 17, map[Revision]struct{}{})
+	require.Len(t, ki.generations, 1)
+	require.Equal(t, []Revision{{17, 0}, {18, 0}}, ki.generations[0].revs)
+}
+
 func TestKeyIndexGet(t *testing.T) {
 	// key: "foo"
 	// modified: 16
diff --git a/server/storage/mvcc/kv_test.go b/server/storage/mvcc/kv_test.go
index c727b444a..790b53439 100644
--- a/server/storage/mvcc/kv_test.go
+++ b/server/storage/mvcc/kv_test.go
@@ -25,6 +25,7 @@ import (
 
 	"github.com/prometheus/client_golang/prometheus"
 	dto "github.com/prometheus/client_model/go"
+	"github.com/stretchr/testify/assert"
 	"go.uber.org/zap/zaptest"
 
 	"go.etcd.io/etcd/api/v3/mvccpb"
@@ -657,6 +658,8 @@ func TestKVHash(t *testing.T) {
 }
 
 func TestKVRestore(t *testing.T) {
+	compactBatchLimit := 5
+
 	tests := []func(kv KV){
 		func(kv KV) {
 			kv.Put([]byte("foo"), []byte("bar0"), 1)
@@ -674,10 +677,23 @@ func TestKVRestore(t *testing.T) {
 			kv.Put([]byte("foo"), []byte("bar1"), 2)
 			kv.Compact(traceutil.TODO(), 1)
 		},
+		func(kv KV) { // after restore, foo1 key only has tombstone revision
+			kv.Put([]byte("foo1"), []byte("bar1"), 0)
+			kv.Put([]byte("foo2"), []byte("bar2"), 0)
+			kv.Put([]byte("foo3"), []byte("bar3"), 0)
+			kv.Put([]byte("foo4"), []byte("bar4"), 0)
+			kv.Put([]byte("foo5"), []byte("bar5"), 0)
+			_, delAtRev := kv.DeleteRange([]byte("foo1"), nil)
+			assert.Equal(t, int64(7), delAtRev)
+
+			// after compaction and restore, foo1 key only has tombstone revision
+			ch, _ := kv.Compact(traceutil.TODO(), delAtRev)
+			<-ch
+		},
 	}
 	for i, tt := range tests {
 		b, _ := betesting.NewDefaultTmpBackend(t)
-		s := NewStore(zaptest.NewLogger(t), b, &lease.FakeLessor{}, StoreConfig{})
+		s := NewStore(zaptest.NewLogger(t), b, &lease.FakeLessor{}, StoreConfig{CompactionBatchLimit: compactBatchLimit})
 		tt(s)
 		var kvss [][]mvccpb.KeyValue
 		for k := int64(0); k < 10; k++ {
@@ -689,7 +705,7 @@ func TestKVRestore(t *testing.T) {
 		s.Close()
 
 		// ns should recover the previous state from backend.
-		ns := NewStore(zaptest.NewLogger(t), b, &lease.FakeLessor{}, StoreConfig{})
+		ns := NewStore(zaptest.NewLogger(t), b, &lease.FakeLessor{}, StoreConfig{CompactionBatchLimit: compactBatchLimit})
 
 		if keysRestore := readGaugeInt(keysGauge); keysBefore != keysRestore {
 			t.Errorf("#%d: got %d key count, expected %d", i, keysRestore, keysBefore)
diff --git a/server/storage/schema/schema_test.go b/server/storage/schema/schema_test.go
index e87f0ff5b..5bcbbe82b 100644
--- a/server/storage/schema/schema_test.go
+++ b/server/storage/schema/schema_test.go
@@ -24,7 +24,6 @@ import (
 	"go.uber.org/zap"
 
 	"go.etcd.io/etcd/api/v3/etcdserverpb"
-	"go.etcd.io/etcd/api/v3/membershippb"
 	"go.etcd.io/etcd/api/v3/version"
 	"go.etcd.io/etcd/server/v3/storage/backend"
 	betesting "go.etcd.io/etcd/server/v3/storage/backend/testing"
@@ -186,7 +185,7 @@ func TestMigrate(t *testing.T) {
 			version:       version.V3_6,
 			targetVersion: version.V3_5,
 			walEntries: []etcdserverpb.InternalRaftRequest{
-				{ClusterVersionSet: &membershippb.ClusterVersionSetRequest{Ver: "3.6.0"}},
+				{DowngradeVersionTest: &etcdserverpb.DowngradeVersionTestRequest{Ver: "3.6.0"}},
 			},
 			expectVersion:  &version.V3_6,
 			expectError:    true,
diff --git a/server/storage/wal/version_test.go b/server/storage/wal/version_test.go
index 8e7e83897..1ee89dd36 100644
--- a/server/storage/wal/version_test.go
+++ b/server/storage/wal/version_test.go
@@ -35,8 +35,11 @@ func TestEtcdVersionFromEntry(t *testing.T) {
 	raftReq := etcdserverpb.InternalRaftRequest{Header: &etcdserverpb.RequestHeader{AuthRevision: 1}}
 	normalRequestData := pbutil.MustMarshal(&raftReq)
 
-	clusterVersionV3_6Req := etcdserverpb.InternalRaftRequest{ClusterVersionSet: &membershippb.ClusterVersionSetRequest{Ver: "3.6.0"}}
-	clusterVersionV3_6Data := pbutil.MustMarshal(&clusterVersionV3_6Req)
+	downgradeVersionTestV3_6Req := etcdserverpb.InternalRaftRequest{DowngradeVersionTest: &etcdserverpb.DowngradeVersionTestRequest{Ver: "3.6.0"}}
+	downgradeVersionTestV3_6Data := pbutil.MustMarshal(&downgradeVersionTestV3_6Req)
+
+	downgradeVersionTestV3_7Req := etcdserverpb.InternalRaftRequest{DowngradeVersionTest: &etcdserverpb.DowngradeVersionTestRequest{Ver: "3.7.0"}}
+	downgradeVersionTestV3_7Data := pbutil.MustMarshal(&downgradeVersionTestV3_7Req)
 
 	confChange := raftpb.ConfChange{Type: raftpb.ConfChangeAddLearnerNode}
 	confChangeData := pbutil.MustMarshal(&confChange)
@@ -60,15 +63,25 @@ func TestEtcdVersionFromEntry(t *testing.T) {
 			expect: &version.V3_1,
 		},
 		{
-			name: "Setting cluster version implies version within",
+			name: "Setting downgradeTest version to 3.6 implies version within WAL",
 			input: raftpb.Entry{
 				Term:  1,
 				Index: 2,
 				Type:  raftpb.EntryNormal,
-				Data:  clusterVersionV3_6Data,
+				Data:  downgradeVersionTestV3_6Data,
 			},
 			expect: &version.V3_6,
 		},
+		{
+			name: "Setting downgradeTest version to 3.7 implies version within WAL",
+			input: raftpb.Entry{
+				Term:  1,
+				Index: 2,
+				Type:  raftpb.EntryNormal,
+				Data:  downgradeVersionTestV3_7Data,
+			},
+			expect: &version.V3_7,
+		},
 		{
 			name: "Using ConfigChange implies v3.0",
 			input: raftpb.Entry{
diff --git a/tests/common/auth_test.go b/tests/common/auth_test.go
index c9544b907..4570824ea 100644
--- a/tests/common/auth_test.go
+++ b/tests/common/auth_test.go
@@ -97,8 +97,10 @@ func TestAuthGracefulDisable(t *testing.T) {
 		donec := make(chan struct{})
 		rootAuthClient := testutils.MustClient(clus.Client(WithAuth(rootUserName, rootPassword)))
 
+		startedC := make(chan struct{}, 1)
 		go func() {
 			defer close(donec)
+			defer close(startedC)
 			// sleep a bit to let the watcher connects while auth is still enabled
 			time.Sleep(time.Second)
 			// now disable auth...
@@ -112,10 +114,13 @@ func TestAuthGracefulDisable(t *testing.T) {
 				t.Errorf("failed to restart member %v", err)
 				return
 			}
+			startedC <- struct{}{}
 			// the watcher should still work after reconnecting
 			assert.NoErrorf(t, rootAuthClient.Put(ctx, "key", "value", config.PutOptions{}), "failed to put key value")
 		}()
 
+		<-startedC
+
 		wCtx, wCancel := context.WithCancel(ctx)
 		defer wCancel()
 
diff --git a/tests/e2e/cluster_downgrade_test.go b/tests/e2e/cluster_downgrade_test.go
index 086b80894..341f85282 100644
--- a/tests/e2e/cluster_downgrade_test.go
+++ b/tests/e2e/cluster_downgrade_test.go
@@ -37,23 +37,47 @@ import (
 	"go.etcd.io/etcd/tests/v3/framework/e2e"
 )
 
+type CancellationState int
+
+const (
+	noCancellation CancellationState = iota
+	cancelRightBeforeEnable
+	cancelRightAfterEnable
+)
+
 func TestDowngradeUpgradeClusterOf1(t *testing.T) {
-	testDowngradeUpgrade(t, 1, false)
+	testDowngradeUpgrade(t, 1, false, noCancellation)
 }
 
 func TestDowngradeUpgradeClusterOf3(t *testing.T) {
-	testDowngradeUpgrade(t, 3, false)
+	testDowngradeUpgrade(t, 3, false, noCancellation)
 }
 
 func TestDowngradeUpgradeClusterOf1WithSnapshot(t *testing.T) {
-	testDowngradeUpgrade(t, 1, true)
+	testDowngradeUpgrade(t, 1, true, noCancellation)
 }
 
 func TestDowngradeUpgradeClusterOf3WithSnapshot(t *testing.T) {
-	testDowngradeUpgrade(t, 3, true)
+	testDowngradeUpgrade(t, 3, true, noCancellation)
+}
+
+func TestDowngradeCancellationWithoutEnablingClusterOf1(t *testing.T) {
+	testDowngradeUpgrade(t, 1, false, cancelRightBeforeEnable)
+}
+
+func TestDowngradeCancellationRightAfterEnablingClusterOf1(t *testing.T) {
+	testDowngradeUpgrade(t, 1, false, cancelRightAfterEnable)
+}
+
+func TestDowngradeCancellationWithoutEnablingClusterOf3(t *testing.T) {
+	testDowngradeUpgrade(t, 3, false, cancelRightBeforeEnable)
 }
 
-func testDowngradeUpgrade(t *testing.T, clusterSize int, triggerSnapshot bool) {
+func TestDowngradeCancellationRightAfterEnablingClusterOf3(t *testing.T) {
+	testDowngradeUpgrade(t, 3, false, cancelRightAfterEnable)
+}
+
+func testDowngradeUpgrade(t *testing.T, clusterSize int, triggerSnapshot bool, triggerCancellation CancellationState) {
 	currentEtcdBinary := e2e.BinPath.Etcd
 	lastReleaseBinary := e2e.BinPath.EtcdLastRelease
 	if !fileutil.Exist(lastReleaseBinary) {
@@ -107,9 +131,26 @@ func testDowngradeUpgrade(t *testing.T, clusterSize int, triggerSnapshot bool) {
 	require.NoError(t, err)
 	beforeMembers, beforeKV := getMembersAndKeys(t, cc)
 
+	if triggerCancellation == cancelRightBeforeEnable {
+		t.Logf("Cancelling downgrade before enabling")
+		e2e.DowngradeCancel(t, epc)
+		t.Log("Downgrade cancelled, validating if cluster is in the right state")
+		e2e.ValidateMemberVersions(t, epc, generateIdenticalVersions(clusterSize, currentVersionStr))
+
+		return // No need to perform downgrading, end the test here
+	}
 	e2e.DowngradeEnable(t, epc, lastVersion)
+	if triggerCancellation == cancelRightAfterEnable {
+		t.Logf("Cancelling downgrade right after enabling (no node is downgraded yet)")
+		e2e.DowngradeCancel(t, epc)
+		t.Log("Downgrade cancelled, validating if cluster is in the right state")
+		e2e.ValidateMemberVersions(t, epc, generateIdenticalVersions(clusterSize, currentVersionStr))
+		return // No need to perform downgrading, end the test here
+	}
+
 	t.Logf("Starting downgrade process to %q", lastVersionStr)
-	e2e.DowngradeUpgradeMembers(t, nil, epc, len(epc.Procs), currentVersion, lastClusterVersion)
+	err = e2e.DowngradeUpgradeMembers(t, nil, epc, len(epc.Procs), currentVersion, lastClusterVersion)
+	require.NoError(t, err)
 	e2e.AssertProcessLogs(t, leader(t, epc), "the cluster has been downgraded")
 
 	t.Log("Downgrade complete")
@@ -135,7 +176,8 @@ func testDowngradeUpgrade(t *testing.T, clusterSize int, triggerSnapshot bool) {
 	beforeMembers, beforeKV = getMembersAndKeys(t, cc)
 
 	t.Logf("Starting upgrade process to %q", currentVersionStr)
-	e2e.DowngradeUpgradeMembers(t, nil, epc, len(epc.Procs), lastClusterVersion, currentVersion)
+	err = e2e.DowngradeUpgradeMembers(t, nil, epc, len(epc.Procs), lastClusterVersion, currentVersion)
+	require.NoError(t, err)
 	t.Log("Upgrade complete")
 
 	afterMembers, afterKV = getMembersAndKeys(t, cc)
@@ -233,3 +275,17 @@ func getMembersAndKeys(t *testing.T, cc *e2e.EtcdctlV3) (*clientv3.MemberListRes
 
 	return members, kvs
 }
+
+func generateIdenticalVersions(clusterSize int, currentVersion string) []*version.Versions {
+	ret := make([]*version.Versions, clusterSize)
+
+	for i := range clusterSize {
+		ret[i] = &version.Versions{
+			Cluster: currentVersion,
+			Server:  currentVersion,
+			Storage: currentVersion,
+		}
+	}
+
+	return ret
+}
diff --git a/tests/e2e/metrics_test.go b/tests/e2e/metrics_test.go
index ee3009632..184a430d6 100644
--- a/tests/e2e/metrics_test.go
+++ b/tests/e2e/metrics_test.go
@@ -15,11 +15,17 @@
 package e2e
 
 import (
+	"bytes"
 	"context"
 	"fmt"
+	"io"
+	"net/http"
+	"net/url"
 	"testing"
 	"time"
 
+	dto "github.com/prometheus/client_model/go"
+	"github.com/prometheus/common/expfmt"
 	"github.com/stretchr/testify/require"
 
 	"go.etcd.io/etcd/api/v3/version"
@@ -126,3 +132,239 @@ func expectLearnerMetric(cx ctlCtx, procIdx int, expectMetric string) {
 	args := e2e.CURLPrefixArgsCluster(cx.epc.Cfg, cx.epc.Procs[procIdx], "GET", e2e.CURLReq{Endpoint: "/metrics"})
 	require.NoError(cx.t, e2e.SpawnWithExpectsContext(ctx, args, nil, expect.ExpectedResponse{Value: expectMetric}))
 }
+
+func TestNoMetricsMissing(t *testing.T) {
+	var (
+		// Note the list doesn't contain all the metrics, because the
+		// labelled metrics won't be exposed by prometheus by default.
+		// They are only exposed when at least one value with labels
+		// is set.
+		basicMetrics = []string{
+			"etcd_cluster_version",
+			"etcd_debugging_auth_revision",
+			"etcd_debugging_disk_backend_commit_rebalance_duration_seconds",
+			"etcd_debugging_disk_backend_commit_spill_duration_seconds",
+			"etcd_debugging_disk_backend_commit_write_duration_seconds",
+			"etcd_debugging_lease_granted_total",
+			"etcd_debugging_lease_renewed_total",
+			"etcd_debugging_lease_revoked_total",
+			"etcd_debugging_lease_ttl_total",
+			"etcd_debugging_mvcc_compact_revision",
+			"etcd_debugging_mvcc_current_revision",
+			"etcd_debugging_mvcc_db_compaction_keys_total",
+			"etcd_debugging_mvcc_db_compaction_last",
+			"etcd_debugging_mvcc_db_compaction_pause_duration_milliseconds",
+			"etcd_debugging_mvcc_db_compaction_total_duration_milliseconds",
+			"etcd_debugging_mvcc_events_total",
+			"etcd_debugging_mvcc_index_compaction_pause_duration_milliseconds",
+			"etcd_debugging_mvcc_keys_total",
+			"etcd_debugging_mvcc_pending_events_total",
+			"etcd_debugging_mvcc_slow_watcher_total",
+			"etcd_debugging_mvcc_total_put_size_in_bytes",
+			"etcd_debugging_mvcc_watch_stream_total",
+			"etcd_debugging_mvcc_watcher_total",
+			"etcd_debugging_server_lease_expired_total",
+			"etcd_debugging_snap_save_marshalling_duration_seconds",
+			"etcd_debugging_snap_save_total_duration_seconds",
+			"etcd_debugging_store_expires_total",
+			"etcd_debugging_store_reads_total",
+			"etcd_debugging_store_watch_requests_total",
+			"etcd_debugging_store_watchers",
+			"etcd_debugging_store_writes_total",
+			"etcd_disk_backend_commit_duration_seconds",
+			"etcd_disk_backend_defrag_duration_seconds",
+			"etcd_disk_backend_snapshot_duration_seconds",
+			"etcd_disk_defrag_inflight",
+			"etcd_disk_wal_fsync_duration_seconds",
+			"etcd_disk_wal_write_bytes_total",
+			"etcd_disk_wal_write_duration_seconds",
+			"etcd_grpc_proxy_cache_hits_total",
+			"etcd_grpc_proxy_cache_keys_total",
+			"etcd_grpc_proxy_cache_misses_total",
+			"etcd_grpc_proxy_events_coalescing_total",
+			"etcd_grpc_proxy_watchers_coalescing_total",
+			"etcd_mvcc_db_open_read_transactions",
+			"etcd_mvcc_db_total_size_in_bytes",
+			"etcd_mvcc_db_total_size_in_use_in_bytes",
+			"etcd_mvcc_delete_total",
+			"etcd_mvcc_hash_duration_seconds",
+			"etcd_mvcc_hash_rev_duration_seconds",
+			"etcd_mvcc_put_total",
+			"etcd_mvcc_range_total",
+			"etcd_mvcc_txn_total",
+			"etcd_network_client_grpc_received_bytes_total",
+			"etcd_network_client_grpc_sent_bytes_total",
+			"etcd_network_known_peers",
+			"etcd_server_apply_duration_seconds",
+			"etcd_server_client_requests_total",
+			"etcd_server_go_version",
+			"etcd_server_has_leader",
+			"etcd_server_health_failures",
+			"etcd_server_health_success",
+			"etcd_server_heartbeat_send_failures_total",
+			"etcd_server_id",
+			"etcd_server_is_leader",
+			"etcd_server_is_learner",
+			"etcd_server_leader_changes_seen_total",
+			"etcd_server_learner_promote_successes",
+			"etcd_server_proposals_applied_total",
+			"etcd_server_proposals_committed_total",
+			"etcd_server_proposals_failed_total",
+			"etcd_server_proposals_pending",
+			"etcd_server_quota_backend_bytes",
+			"etcd_server_range_duration_seconds",
+			"etcd_server_read_indexes_failed_total",
+			"etcd_server_slow_apply_total",
+			"etcd_server_slow_read_indexes_total",
+			"etcd_server_snapshot_apply_in_progress_total",
+			"etcd_server_version",
+			"etcd_snap_db_fsync_duration_seconds",
+			"etcd_snap_db_save_total_duration_seconds",
+			"etcd_snap_fsync_duration_seconds",
+			"go_gc_duration_seconds",
+			"go_gc_gogc_percent",
+			"go_gc_gomemlimit_bytes",
+			"go_goroutines",
+			"go_info",
+			"go_memstats_alloc_bytes",
+			"go_memstats_alloc_bytes_total",
+			"go_memstats_buck_hash_sys_bytes",
+			"go_memstats_frees_total",
+			"go_memstats_gc_sys_bytes",
+			"go_memstats_heap_alloc_bytes",
+			"go_memstats_heap_idle_bytes",
+			"go_memstats_heap_inuse_bytes",
+			"go_memstats_heap_objects",
+			"go_memstats_heap_released_bytes",
+			"go_memstats_heap_sys_bytes",
+			"go_memstats_last_gc_time_seconds",
+			"go_memstats_mallocs_total",
+			"go_memstats_mcache_inuse_bytes",
+			"go_memstats_mcache_sys_bytes",
+			"go_memstats_mspan_inuse_bytes",
+			"go_memstats_mspan_sys_bytes",
+			"go_memstats_next_gc_bytes",
+			"go_memstats_other_sys_bytes",
+			"go_memstats_stack_inuse_bytes",
+			"go_memstats_stack_sys_bytes",
+			"go_memstats_sys_bytes",
+			"go_sched_gomaxprocs_threads",
+			"go_threads",
+			"grpc_server_handled_total",
+			"grpc_server_msg_received_total",
+			"grpc_server_msg_sent_total",
+			"grpc_server_started_total",
+			"os_fd_limit",
+			"os_fd_used",
+			"promhttp_metric_handler_requests_in_flight",
+			"promhttp_metric_handler_requests_total",
+		}
+		extraMultipleMemberClusterMetrics = []string{
+			"etcd_network_active_peers",
+			"etcd_network_peer_received_bytes_total",
+			"etcd_network_peer_sent_bytes_total",
+		}
+		extraExtensiveMetrics = []string{"grpc_server_handling_seconds"}
+	)
+
+	testCases := []struct {
+		name            string
+		options         []e2e.EPClusterOption
+		expectedMetrics []string
+	}{
+		{
+			name: "basic metrics of 1 member cluster",
+			options: []e2e.EPClusterOption{
+				e2e.WithClusterSize(1),
+			},
+			expectedMetrics: basicMetrics,
+		},
+		{
+			name: "basic metrics of 3 member cluster",
+			options: []e2e.EPClusterOption{
+				e2e.WithClusterSize(3),
+			},
+			expectedMetrics: append(basicMetrics, extraMultipleMemberClusterMetrics...),
+		},
+		{
+			name: "extensive metrics of 1 member cluster",
+			options: []e2e.EPClusterOption{
+				e2e.WithClusterSize(1),
+				e2e.WithExtensiveMetrics(),
+			},
+			expectedMetrics: append(basicMetrics, extraExtensiveMetrics...),
+		},
+		{
+			name: "extensive metrics of 3 member cluster",
+			options: []e2e.EPClusterOption{
+				e2e.WithClusterSize(3),
+				e2e.WithExtensiveMetrics(),
+			},
+			expectedMetrics: append(append(basicMetrics, extraExtensiveMetrics...), extraMultipleMemberClusterMetrics...),
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			e2e.BeforeTest(t)
+			ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+			defer cancel()
+
+			epc, err := e2e.NewEtcdProcessCluster(ctx, t, tc.options...)
+			require.NoError(t, err)
+			defer epc.Close()
+
+			c := epc.Procs[0].Etcdctl()
+			for i := 0; i < 3; i++ {
+				err = c.Put(ctx, fmt.Sprintf("key_%d", i), fmt.Sprintf("value_%d", i), config.PutOptions{})
+				require.NoError(t, err)
+			}
+			_, err = c.Get(ctx, "k", config.GetOptions{})
+			require.NoError(t, err)
+
+			metricsURL, err := url.JoinPath(epc.Procs[0].Config().ClientURL, "metrics")
+			require.NoError(t, err)
+
+			mfs, err := getMetrics(metricsURL)
+			require.NoError(t, err)
+
+			var missingMetrics []string
+			for _, metrics := range tc.expectedMetrics {
+				if _, ok := mfs[metrics]; !ok {
+					missingMetrics = append(missingMetrics, metrics)
+				}
+			}
+			require.Emptyf(t, missingMetrics, "Some metrics are missing: %v", missingMetrics)
+
+			// Please keep the log below to generate the expected metrics.
+			// t.Logf("All metrics: %v", formatMetrics(slices.Sorted(maps.Keys(mfs))))
+		})
+	}
+}
+
+func getMetrics(metricsURL string) (map[string]*dto.MetricFamily, error) {
+	httpClient := http.Client{Transport: &http.Transport{}}
+	resp, err := httpClient.Get(metricsURL)
+	if err != nil {
+		return nil, err
+	}
+	defer resp.Body.Close()
+
+	data, err := io.ReadAll(resp.Body)
+	if err != nil {
+		return nil, err
+	}
+
+	var parser expfmt.TextParser
+	return parser.TextToMetricFamilies(bytes.NewReader(data))
+}
+
+// formatMetrics is only for test purpose
+/*func formatMetrics(metrics []string) string {
+	quoted := make([]string, len(metrics))
+	for i, s := range metrics {
+		quoted[i] = fmt.Sprintf(`"%s",`, s)
+	}
+
+	return fmt.Sprintf("[]string{\n%s\n}", strings.Join(quoted, "\n"))
+}*/
diff --git a/tests/e2e/runtime_reconfiguration_test.go b/tests/e2e/runtime_reconfiguration_test.go
index 308808a53..1736126e6 100644
--- a/tests/e2e/runtime_reconfiguration_test.go
+++ b/tests/e2e/runtime_reconfiguration_test.go
@@ -18,6 +18,7 @@ package e2e
 
 import (
 	"context"
+	"strings"
 	"testing"
 	"time"
 
@@ -177,7 +178,16 @@ func addMemberAsLearnerAndPromote(ctx context.Context, t *testing.T, epc *e2e.Et
 
 	id, err := epc.StartNewProc(ctx, nil, t, true /* addAsLearner */)
 	require.NoError(t, err)
-	_, err = epc.Etcdctl(e2e.WithEndpoints(endpoints)).MemberPromote(ctx, id)
+
+	attempt := 0
+	for attempt < 3 {
+		_, err = epc.Etcdctl(e2e.WithEndpoints(endpoints)).MemberPromote(ctx, id)
+		if err == nil || !strings.Contains(err.Error(), "can only promote a learner member which is in sync with leader") {
+			break
+		}
+		time.Sleep(100 * time.Millisecond)
+		attempt++
+	}
 	require.NoError(t, err)
 
 	newLearnerMemberProc := epc.Procs[len(epc.Procs)-1]
diff --git a/tests/e2e/watch_test.go b/tests/e2e/watch_test.go
index f167b8f87..758177c64 100644
--- a/tests/e2e/watch_test.go
+++ b/tests/e2e/watch_test.go
@@ -91,7 +91,7 @@ func TestWatchDelayForPeriodicProgressNotification(t *testing.T) {
 		tc := tc
 		cfg := e2e.DefaultConfig()
 		cfg.ClusterSize = 1
-		cfg.ServerConfig.ExperimentalWatchProgressNotifyInterval = watchResponsePeriod
+		cfg.ServerConfig.WatchProgressNotifyInterval = watchResponsePeriod
 		cfg.Client = tc.client
 		cfg.ClientHTTPSeparate = tc.clientHTTPSeparate
 		t.Run(tc.name, func(t *testing.T) {
@@ -488,3 +488,77 @@ func testStartWatcherFromCompactedRevision(t *testing.T, performCompactOnTombsto
 		}
 	}
 }
+
+// TestResumeCompactionOnTombstone verifies whether a deletion event is preserved
+// when etcd restarts and resumes compaction on a key that only has a tombstone revision.
+func TestResumeCompactionOnTombstone(t *testing.T) {
+	e2e.BeforeTest(t)
+
+	ctx := context.Background()
+	compactBatchLimit := 5
+
+	cfg := e2e.DefaultConfig()
+	clus, err := e2e.NewEtcdProcessCluster(context.Background(),
+		t,
+		e2e.WithConfig(cfg),
+		e2e.WithClusterSize(1),
+		e2e.WithCompactionBatchLimit(compactBatchLimit),
+		e2e.WithGoFailEnabled(true),
+		e2e.WithWatchProcessNotifyInterval(100*time.Millisecond),
+	)
+	require.NoError(t, err)
+	defer clus.Close()
+
+	c1 := newClient(t, clus.EndpointsGRPC(), cfg.Client)
+	defer c1.Close()
+
+	keyPrefix := "/key-"
+	for i := 0; i < compactBatchLimit; i++ {
+		key := fmt.Sprintf("%s%d", keyPrefix, i)
+		value := fmt.Sprintf("%d", i)
+
+		t.Logf("PUT key=%s, val=%s", key, value)
+		_, err = c1.KV.Put(ctx, key, value)
+		require.NoError(t, err)
+	}
+
+	firstKey := keyPrefix + "0"
+	t.Logf("DELETE key=%s", firstKey)
+	deleteResp, err := c1.KV.Delete(ctx, firstKey)
+	require.NoError(t, err)
+
+	var deleteEvent *clientv3.Event
+	select {
+	case watchResp := <-c1.Watch(ctx, firstKey, clientv3.WithRev(deleteResp.Header.Revision)):
+		require.Len(t, watchResp.Events, 1)
+
+		require.Equal(t, mvccpb.DELETE, watchResp.Events[0].Type)
+		deletedKey := string(watchResp.Events[0].Kv.Key)
+		require.Equal(t, firstKey, deletedKey)
+
+		deleteEvent = watchResp.Events[0]
+	case <-time.After(100 * time.Millisecond):
+		t.Fatal("timed out getting watch response")
+	}
+
+	require.NoError(t, clus.Procs[0].Failpoints().SetupHTTP(ctx, "compactBeforeSetFinishedCompact", `panic`))
+
+	t.Logf("COMPACT rev=%d", deleteResp.Header.Revision)
+	_, err = c1.KV.Compact(ctx, deleteResp.Header.Revision, clientv3.WithCompactPhysical())
+	require.Error(t, err)
+
+	require.NoError(t, clus.Restart(ctx))
+
+	c2 := newClient(t, clus.EndpointsGRPC(), cfg.Client)
+	defer c2.Close()
+
+	watchChan := c2.Watch(ctx, firstKey, clientv3.WithRev(deleteResp.Header.Revision))
+	select {
+	case watchResp := <-watchChan:
+		require.Equal(t, []*clientv3.Event{deleteEvent}, watchResp.Events)
+	case <-time.After(100 * time.Millisecond):
+		// we care only about the first response, but have an
+		// escape hatch in case the watch response is delayed.
+		t.Fatal("timed out getting watch response")
+	}
+}
diff --git a/tests/framework/e2e/cluster.go b/tests/framework/e2e/cluster.go
index 3da0ff9a8..282e96149 100644
--- a/tests/framework/e2e/cluster.go
+++ b/tests/framework/e2e/cluster.go
@@ -385,6 +385,10 @@ func WithCompactionSleepInterval(time time.Duration) EPClusterOption {
 }
 
 func WithWatchProcessNotifyInterval(interval time.Duration) EPClusterOption {
+	return func(c *EtcdProcessClusterConfig) { c.ServerConfig.WatchProgressNotifyInterval = interval }
+}
+
+func WithExperimentalWatchProcessNotifyInterval(interval time.Duration) EPClusterOption {
 	return func(c *EtcdProcessClusterConfig) { c.ServerConfig.ExperimentalWatchProgressNotifyInterval = interval }
 }
 
@@ -396,6 +400,26 @@ func WithPeerProxy(enabled bool) EPClusterOption {
 	return func(c *EtcdProcessClusterConfig) { c.PeerProxy = enabled }
 }
 
+func WithClientHTTPSeparate(enabled bool) EPClusterOption {
+	return func(c *EtcdProcessClusterConfig) { c.ClientHTTPSeparate = enabled }
+}
+
+func WithForceNewCluster(enabled bool) EPClusterOption {
+	return func(c *EtcdProcessClusterConfig) { c.ServerConfig.ForceNewCluster = enabled }
+}
+
+func WithMetricsURLScheme(scheme string) EPClusterOption {
+	return func(c *EtcdProcessClusterConfig) { c.MetricsURLScheme = scheme }
+}
+
+func WithCipherSuites(suites []string) EPClusterOption {
+	return func(c *EtcdProcessClusterConfig) { c.ServerConfig.CipherSuites = suites }
+}
+
+func WithExtensiveMetrics() EPClusterOption {
+	return func(c *EtcdProcessClusterConfig) { c.ServerConfig.Metrics = "extensive" }
+}
+
 // NewEtcdProcessCluster launches a new cluster from etcd processes, returning
 // a new EtcdProcessCluster once all nodes are ready to accept client requests.
 func NewEtcdProcessCluster(ctx context.Context, t testing.TB, opts ...EPClusterOption) (*EtcdProcessCluster, error) {
@@ -581,7 +605,7 @@ func (cfg *EtcdProcessClusterConfig) EtcdServerProcessConfig(tb testing.TB, i in
 	}
 
 	if cfg.ServerConfig.ForceNewCluster {
-		args = append(args, "--force-new-cluster")
+		args = append(args, "--force-new-cluster=true")
 	}
 	if cfg.ServerConfig.QuotaBackendBytes > 0 {
 		args = append(args,
@@ -592,7 +616,7 @@ func (cfg *EtcdProcessClusterConfig) EtcdServerProcessConfig(tb testing.TB, i in
 		args = append(args, "--strict-reconfig-check=false")
 	}
 	if cfg.EnableV2 {
-		args = append(args, "--enable-v2")
+		args = append(args, "--enable-v2=true")
 	}
 	var murl string
 	if cfg.MetricsURLScheme != "" {
@@ -1062,9 +1086,8 @@ func (epc *EtcdProcessCluster) WaitMembersForLeader(ctx context.Context, t testi
 				if strings.Contains(err.Error(), "connection refused") {
 					// if member[i] has stopped
 					continue
-				} else {
-					t.Fatal(err)
 				}
+				t.Fatal(err)
 			}
 			members[resp[0].Header.MemberId] = i
 			leaders[resp[0].Leader] = struct{}{}
diff --git a/tests/framework/e2e/cluster_test.go b/tests/framework/e2e/cluster_test.go
index 7222460ef..fc9787add 100644
--- a/tests/framework/e2e/cluster_test.go
+++ b/tests/framework/e2e/cluster_test.go
@@ -28,20 +28,25 @@ func TestEtcdServerProcessConfig(t *testing.T) {
 	tcs := []struct {
 		name                 string
 		config               *EtcdProcessClusterConfig
+		expectArgsEquals     []string
 		expectArgsNotContain []string
 		expectArgsContain    []string
 		mockBinaryVersion    *semver.Version
 	}{
 		{
 			name:   "Default",
-			config: NewConfig(),
-			expectArgsContain: []string{
+			config: NewConfig(WithDataDirPath("/tmp/a")),
+			expectArgsEquals: []string{
+				"--name=TestEtcdServerProcessConfigDefault-test-0",
 				"--listen-client-urls=http://localhost:0",
 				"--advertise-client-urls=http://localhost:0",
 				"--listen-peer-urls=http://localhost:1",
 				"--initial-advertise-peer-urls=http://localhost:1",
 				"--initial-cluster-token=new",
+				"--data-dir",
+				"/tmp/a/member-0",
 				"--snapshot-count=10000",
+				"--initial-cluster-token=new",
 			},
 		},
 		{
@@ -95,6 +100,121 @@ func TestEtcdServerProcessConfig(t *testing.T) {
 			},
 			mockBinaryVersion: &v3_5_12,
 		},
+		{
+			name:   "ClientHTTPSeparate",
+			config: NewConfig(WithClientHTTPSeparate(true)),
+			expectArgsContain: []string{
+				"--listen-client-http-urls=http://localhost:4",
+			},
+		},
+		{
+			name:   "ForceNewCluster",
+			config: NewConfig(WithForceNewCluster(true)),
+			expectArgsContain: []string{
+				"--force-new-cluster=true",
+			},
+		},
+		{
+			name:   "EnableV2",
+			config: NewConfig(WithEnableV2(true)),
+			expectArgsContain: []string{
+				"--enable-v2=true",
+			},
+		},
+		{
+			name:   "MetricsURL",
+			config: NewConfig(WithMetricsURLScheme("http")),
+			expectArgsContain: []string{
+				"--listen-metrics-urls=http://localhost:2",
+			},
+		},
+		{
+			name:   "Discovery",
+			config: NewConfig(WithDiscovery("123")),
+			expectArgsContain: []string{
+				"--discovery=123",
+			},
+		},
+		{
+			name:   "ClientTLS",
+			config: NewConfig(WithClientConnType(ClientTLS)),
+			expectArgsContain: []string{
+				"--cert-file",
+				"--key-file",
+				"--trusted-ca-file",
+			},
+			expectArgsNotContain: []string{
+				"--auto-tls",
+				"--client-cert-auth",
+			},
+		},
+		{
+			name:   "ClientTLSCA",
+			config: NewConfig(WithClientConnType(ClientTLS), WithClientCertAuthority(true)),
+			expectArgsContain: []string{
+				"--cert-file",
+				"--key-file",
+				"--trusted-ca-file",
+				"--client-cert-auth",
+			},
+			expectArgsNotContain: []string{
+				"--auto-tls",
+			},
+		},
+		{
+			name:   "ClientAutoTLS",
+			config: NewConfig(WithClientConnType(ClientTLS), WithClientAutoTLS(true)),
+			expectArgsContain: []string{
+				"--auto-tls",
+			},
+			expectArgsNotContain: []string{
+				"--cert-file",
+				"--key-file",
+				"--trusted-ca-file",
+				"--client-cert-auth",
+			},
+		},
+		{
+			name:   "PeerTLS",
+			config: NewConfig(WithIsPeerTLS(true)),
+			expectArgsContain: []string{
+				"--peer-cert-file",
+				"--peer-key-file",
+				"--peer-trusted-ca-file",
+			},
+			expectArgsNotContain: []string{
+				"--peer-auto-tls",
+				"--peer-client-cert-auth",
+			},
+		},
+		{
+			name:   "PeerAutoTLS",
+			config: NewConfig(WithIsPeerTLS(true), WithIsPeerAutoTLS(true)),
+			expectArgsContain: []string{
+				"--peer-auto-tls",
+			},
+			expectArgsNotContain: []string{
+				"--peer-cert-file",
+				"--peer-key-file",
+				"--peer-trusted-ca-file",
+				"--peer-client-cert-auth",
+			},
+		},
+		{
+			name:   "RevokeCerts",
+			config: NewConfig(WithClientRevokeCerts(true)),
+			expectArgsContain: []string{
+				"--client-crl-file",
+				"--client-cert-auth",
+			},
+		},
+		{
+			name:   "CipherSuites",
+			config: NewConfig(WithCipherSuites([]string{"a", "b"})),
+			expectArgsContain: []string{
+				"--cipher-suites",
+			},
+		},
 	}
 	for _, tc := range tcs {
 		t.Run(tc.name, func(t *testing.T) {
@@ -110,6 +230,9 @@ func TestEtcdServerProcessConfig(t *testing.T) {
 			}
 			setGetVersionFromBinary(t, mockGetVersionFromBinary)
 			args := tc.config.EtcdServerProcessConfig(t, 0).Args
+			if len(tc.expectArgsEquals) != 0 {
+				assert.Equal(t, args, tc.expectArgsEquals)
+			}
 			if len(tc.expectArgsContain) != 0 {
 				assert.Subset(t, args, tc.expectArgsContain)
 			}
diff --git a/tests/framework/e2e/downgrade.go b/tests/framework/e2e/downgrade.go
index 60a45aded..52a7c4c87 100644
--- a/tests/framework/e2e/downgrade.go
+++ b/tests/framework/e2e/downgrade.go
@@ -53,6 +53,17 @@ func DowngradeEnable(t *testing.T, epc *EtcdProcessCluster, ver *semver.Version)
 	t.Log("Cluster is ready for downgrade")
 }
 
+func DowngradeCancel(t *testing.T, epc *EtcdProcessCluster) {
+	t.Logf("etcdctl downgrade cancel")
+	c := epc.Etcdctl()
+	testutils.ExecuteWithTimeout(t, 20*time.Second, func() {
+		err := c.DowngradeCancel(context.TODO())
+		require.NoError(t, err)
+	})
+
+	t.Log("Cluster downgrade cancellation is completed")
+}
+
 func DowngradeUpgradeMembers(t *testing.T, lg *zap.Logger, clus *EtcdProcessCluster, numberOfMembersToChange int, currentVersion, targetVersion *semver.Version) error {
 	if lg == nil {
 		lg = clus.lg
@@ -104,6 +115,13 @@ func DowngradeUpgradeMembers(t *testing.T, lg *zap.Logger, clus *EtcdProcessClus
 	return nil
 }
 
+func ValidateMemberVersions(t *testing.T, epc *EtcdProcessCluster, expect []*version.Versions) {
+	for i := 0; i < len(epc.Procs); i++ {
+		ValidateVersion(t, epc.Cfg, epc.Procs[i], *expect[i])
+	}
+	t.Log("Cluster member version validation after downgrade cancellation is completed")
+}
+
 func ValidateVersion(t *testing.T, cfg *EtcdProcessClusterConfig, member EtcdProcess, expect version.Versions) {
 	testutils.ExecuteWithTimeout(t, 30*time.Second, func() {
 		for {
diff --git a/tests/framework/e2e/etcdctl.go b/tests/framework/e2e/etcdctl.go
index d0c8dc14c..9235ab28d 100644
--- a/tests/framework/e2e/etcdctl.go
+++ b/tests/framework/e2e/etcdctl.go
@@ -86,6 +86,11 @@ func (ctl *EtcdctlV3) DowngradeEnable(ctx context.Context, version string) error
 	return err
 }
 
+func (ctl *EtcdctlV3) DowngradeCancel(ctx context.Context) error {
+	_, err := SpawnWithExpectLines(ctx, ctl.cmdArgs("downgrade", "cancel"), nil, expect.ExpectedResponse{Value: "Downgrade cancel success"})
+	return err
+}
+
 func (ctl *EtcdctlV3) Get(ctx context.Context, key string, o config.GetOptions) (*clientv3.GetResponse, error) {
 	resp := clientv3.GetResponse{}
 	var args []string
diff --git a/tests/framework/integration/bridge.go b/tests/framework/integration/bridge.go
index 5a832c58d..e66f56e1a 100644
--- a/tests/framework/integration/bridge.go
+++ b/tests/framework/integration/bridge.go
@@ -24,6 +24,16 @@ type Dialer interface {
 	Dial() (net.Conn, error)
 }
 
+// Bridge interface exposing methods of the bridge
+type Bridge interface {
+	Close()
+	DropConnections()
+	PauseConnections()
+	UnpauseConnections()
+	Blackhole()
+	Unblackhole()
+}
+
 // bridge proxies connections between listener and dialer, making it possible
 // to disconnect grpc network connections without closing the logical grpc connection.
 type bridge struct {
diff --git a/tests/framework/integration/cluster.go b/tests/framework/integration/cluster.go
index 229611fe2..c81c10d00 100644
--- a/tests/framework/integration/cluster.go
+++ b/tests/framework/integration/cluster.go
@@ -172,9 +172,10 @@ type ClusterConfig struct {
 	LeaseCheckpointPersist  bool
 
 	WatchProgressNotifyInterval time.Duration
-	ExperimentalMaxLearners     int
+	MaxLearners                 int
 	DisableStrictReconfigCheck  bool
 	CorruptCheckTime            time.Duration
+	Metrics                     string
 }
 
 type Cluster struct {
@@ -289,9 +290,10 @@ func (c *Cluster) MustNewMember(t testutil.TB) *Member {
 			LeaseCheckpointInterval:     c.Cfg.LeaseCheckpointInterval,
 			LeaseCheckpointPersist:      c.Cfg.LeaseCheckpointPersist,
 			WatchProgressNotifyInterval: c.Cfg.WatchProgressNotifyInterval,
-			ExperimentalMaxLearners:     c.Cfg.ExperimentalMaxLearners,
+			MaxLearners:                 c.Cfg.MaxLearners,
 			DisableStrictReconfigCheck:  c.Cfg.DisableStrictReconfigCheck,
 			CorruptCheckTime:            c.Cfg.CorruptCheckTime,
+			Metrics:                     c.Cfg.Metrics,
 		})
 	m.DiscoveryURL = c.Cfg.DiscoveryURL
 	return m
@@ -566,7 +568,7 @@ type Member struct {
 	GRPCServerOpts []grpc.ServerOption
 	GRPCServer     *grpc.Server
 	GRPCURL        string
-	GRPCBridge     *bridge
+	GRPCBridge     Bridge
 
 	// ServerClient is a clientv3 that directly calls the etcdserver.
 	ServerClient *clientv3.Client
@@ -614,9 +616,10 @@ type MemberConfig struct {
 	LeaseCheckpointInterval     time.Duration
 	LeaseCheckpointPersist      bool
 	WatchProgressNotifyInterval time.Duration
-	ExperimentalMaxLearners     int
+	MaxLearners                 int
 	DisableStrictReconfigCheck  bool
 	CorruptCheckTime            time.Duration
+	Metrics                     string
 }
 
 // MustNewMember return an inited member with the given name. If peerTLS is
@@ -727,10 +730,11 @@ func MustNewMember(t testutil.TB, mcfg MemberConfig) *Member {
 	}
 	m.WarningApplyDuration = embed.DefaultWarningApplyDuration
 	m.WarningUnaryRequestDuration = embed.DefaultWarningUnaryRequestDuration
-	m.ExperimentalMaxLearners = membership.DefaultMaxLearners
-	if mcfg.ExperimentalMaxLearners != 0 {
-		m.ExperimentalMaxLearners = mcfg.ExperimentalMaxLearners
+	m.MaxLearners = membership.DefaultMaxLearners
+	if mcfg.MaxLearners != 0 {
+		m.MaxLearners = mcfg.MaxLearners
 	}
+	m.Metrics = mcfg.Metrics
 	m.V2Deprecation = config.V2_DEPR_DEFAULT
 	m.GRPCServerRecorder = &grpctesting.GRPCRecorder{}
 
@@ -819,7 +823,7 @@ func (m *Member) clientScheme() string {
 	return ""
 }
 
-func (m *Member) addBridge() (*bridge, error) {
+func (m *Member) addBridge() (Bridge, error) {
 	network, host, port := m.grpcAddr()
 	grpcAddr := net.JoinHostPort(host, m.Port)
 	bridgePort := fmt.Sprintf("%s%s", port, "0")
@@ -840,7 +844,7 @@ func (m *Member) addBridge() (*bridge, error) {
 	return m.GRPCBridge, nil
 }
 
-func (m *Member) Bridge() *bridge {
+func (m *Member) Bridge() Bridge {
 	if !m.UseBridge {
 		m.Logger.Panic("Bridge not available. Please configure using bridge before creating Cluster.")
 	}
diff --git a/tests/go.mod b/tests/go.mod
index 79fb1c7a5..492c177af 100644
--- a/tests/go.mod
+++ b/tests/go.mod
@@ -21,10 +21,11 @@ require (
 	github.com/golang/protobuf v1.5.4
 	github.com/google/go-cmp v0.6.0
 	github.com/grpc-ecosystem/go-grpc-middleware v1.3.0
-	github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0
-	github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1
+	github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1
+	github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.0
 	github.com/prometheus/client_golang v1.20.5
-	github.com/prometheus/common v0.61.0
+	github.com/prometheus/client_model v0.6.1
+	github.com/prometheus/common v0.62.0
 	github.com/soheilhy/cmux v0.1.5
 	github.com/stretchr/testify v1.10.0
 	go.etcd.io/etcd/api/v3 v3.6.0-alpha.0
@@ -37,17 +38,17 @@ require (
 	go.etcd.io/etcd/server/v3 v3.6.0-alpha.0
 	go.etcd.io/gofail v0.2.0
 	go.etcd.io/raft/v3 v3.6.0-beta.0
-	go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.58.0
-	go.opentelemetry.io/otel v1.33.0
-	go.opentelemetry.io/otel/sdk v1.33.0
-	go.opentelemetry.io/otel/trace v1.33.0
+	go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0
+	go.opentelemetry.io/otel v1.34.0
+	go.opentelemetry.io/otel/sdk v1.34.0
+	go.opentelemetry.io/otel/trace v1.34.0
 	go.opentelemetry.io/proto/otlp v1.5.0
 	go.uber.org/zap v1.27.0
 	golang.org/x/crypto v0.32.0
 	golang.org/x/sync v0.10.0
 	golang.org/x/time v0.9.0
-	google.golang.org/grpc v1.69.4
-	google.golang.org/protobuf v1.36.2
+	google.golang.org/grpc v1.70.0
+	google.golang.org/protobuf v1.36.4
 )
 
 require (
@@ -56,7 +57,7 @@ require (
 	github.com/bgentry/speakeasy v0.2.0 // indirect
 	github.com/cenkalti/backoff/v4 v4.3.0 // indirect
 	github.com/cespare/xxhash/v2 v2.3.0 // indirect
-	github.com/cheggaaa/pb/v3 v3.1.5 // indirect
+	github.com/cheggaaa/pb/v3 v3.1.6 // indirect
 	github.com/coreos/go-systemd/v22 v22.5.0 // indirect
 	github.com/creack/pty v1.1.18 // indirect
 	github.com/davecgh/go-spew v1.1.1 // indirect
@@ -65,21 +66,21 @@ require (
 	github.com/go-logr/logr v1.4.2 // indirect
 	github.com/go-logr/stdr v1.2.2 // indirect
 	github.com/gogo/protobuf v1.3.2 // indirect
-	github.com/golang-jwt/jwt/v4 v4.5.1 // indirect
+	github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
 	github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
 	github.com/google/btree v1.1.3 // indirect
 	github.com/google/uuid v1.6.0 // indirect
 	github.com/gorilla/websocket v1.4.2 // indirect
+	github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 // indirect
 	github.com/inconshreveable/mousetrap v1.1.0 // indirect
 	github.com/jonboulle/clockwork v0.5.0 // indirect
 	github.com/klauspost/compress v1.17.9 // indirect
-	github.com/mattn/go-colorable v0.1.13 // indirect
+	github.com/mattn/go-colorable v0.1.14 // indirect
 	github.com/mattn/go-isatty v0.0.20 // indirect
 	github.com/mattn/go-runewidth v0.0.16 // indirect
 	github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
 	github.com/olekukonko/tablewriter v0.0.5 // indirect
 	github.com/pmezard/go-difflib v1.0.0 // indirect
-	github.com/prometheus/client_model v0.6.1 // indirect
 	github.com/prometheus/procfs v0.15.1 // indirect
 	github.com/rivo/uniseg v0.4.7 // indirect
 	github.com/sirupsen/logrus v1.9.3 // indirect
@@ -89,15 +90,15 @@ require (
 	github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 // indirect
 	go.etcd.io/bbolt v1.4.0-beta.0 // indirect
 	go.opentelemetry.io/auto/sdk v1.1.0 // indirect
-	go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0 // indirect
-	go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0 // indirect
-	go.opentelemetry.io/otel/metric v1.33.0 // indirect
+	go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 // indirect
+	go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 // indirect
+	go.opentelemetry.io/otel/metric v1.34.0 // indirect
 	go.uber.org/multierr v1.11.0 // indirect
 	golang.org/x/net v0.34.0 // indirect
 	golang.org/x/sys v0.29.0 // indirect
 	golang.org/x/text v0.21.0 // indirect
-	google.golang.org/genproto/googleapis/api v0.0.0-20250102185135-69823020774d // indirect
-	google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d // indirect
+	google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f // indirect
+	google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect
 	gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
 	sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6 // indirect
diff --git a/tests/go.sum b/tests/go.sum
index e6fbc7b0f..8f13c0c4d 100644
--- a/tests/go.sum
+++ b/tests/go.sum
@@ -13,8 +13,8 @@ github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyY
 github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
 github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
 github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
-github.com/cheggaaa/pb/v3 v3.1.5 h1:QuuUzeM2WsAqG2gMqtzaWithDJv0i+i6UlnwSCI4QLk=
-github.com/cheggaaa/pb/v3 v3.1.5/go.mod h1:CrxkeghYTXi1lQBEI7jSn+3svI3cuc19haAj6jM60XI=
+github.com/cheggaaa/pb/v3 v3.1.6 h1:h0x+vd7EiUohAJ29DJtJy+SNAc55t/elW3jCD086EXk=
+github.com/cheggaaa/pb/v3 v3.1.6/go.mod h1:urxmfVtaxT+9aWk92DbsvXFZtNSWQSO5TRAp+MJ3l1s=
 github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
 github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
 github.com/cockroachdb/datadriven v1.0.2 h1:H9MtNqVoVhvd9nCBwOyDjUEdZCREqbIdCJD93PBm/jA=
@@ -48,8 +48,8 @@ github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/me
 github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
 github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
 github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
-github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo=
-github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
+github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
+github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
 github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
 github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@@ -71,10 +71,12 @@ github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0U
 github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
 github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw=
 github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y=
-github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho=
-github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
-github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 h1:VNqngBF40hVlDloBruUehVYC3ArSgIyScOAyMRqBxRg=
-github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1/go.mod h1:RBRO7fro65R6tjKzYgLAFo0t1QEXY1Dp+i/bvpRiqiQ=
+github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 h1:qnpSQwGEnkcRpTqNOIR6bJbR0gAorgP9CSALpRcKoAA=
+github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1/go.mod h1:lXGCsh6c22WGtjr+qGHj1otzZpV/1kwTMAqkwZsnWRU=
+github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 h1:pRhl55Yx1eC7BZ1N+BBWwnKaMyD8uC+34TLdndZMAKk=
+github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0/go.mod h1:XKMd7iuf/RGPSMJ/U4HP0zS2Z9Fh8Ps9a+6X26m/tmI=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.0 h1:VD1gqscl4nYs1YxVuSdemTrSgTKrwOWDK0FVFMqm+Cg=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.0/go.mod h1:4EgsQoS4TOhJizV+JTFg40qx1Ofh3XmXEQNBpgvNT40=
 github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
 github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
 github.com/jonboulle/clockwork v0.5.0 h1:Hyh9A8u51kptdkR+cqRpT1EebBwTn1oK9YfGYbdFz6I=
@@ -91,9 +93,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
 github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
 github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
 github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
-github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
-github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
-github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
+github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
+github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
 github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
 github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
 github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
@@ -112,8 +113,8 @@ github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/j
 github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
 github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
 github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
-github.com/prometheus/common v0.61.0 h1:3gv/GThfX0cV2lpO7gkTUwZru38mxevy90Bj8YFSRQQ=
-github.com/prometheus/common v0.61.0/go.mod h1:zr29OCN/2BsJRaFwG8QOBr41D6kkchKbpeNH7pAjb/s=
+github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
+github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
 github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
 github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
 github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
@@ -152,22 +153,22 @@ go.etcd.io/raft/v3 v3.6.0-beta.0 h1:MZFQVjCQxPJj5K9oS69Y+atNvYnGNyOQBnroTdw56jQ=
 go.etcd.io/raft/v3 v3.6.0-beta.0/go.mod h1:C2JoekRXfvImSrk5GnqD0aZ3a+cGVRnyem9qqn2DCEw=
 go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
 go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
-go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.58.0 h1:PS8wXpbyaDJQ2VDHHncMe9Vct0Zn1fEjpsjrLxGJoSc=
-go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.58.0/go.mod h1:HDBUsEjOuRC0EzKZ1bSaRGZWUBAzo+MhAcUUORSr4D0=
-go.opentelemetry.io/otel v1.33.0 h1:/FerN9bax5LoK51X/sI0SVYrjSE0/yUL7DpxW4K3FWw=
-go.opentelemetry.io/otel v1.33.0/go.mod h1:SUUkR6csvUQl+yjReHu5uM3EtVV7MBm5FHKRlNx4I8I=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0 h1:Vh5HayB/0HHfOQA7Ctx69E/Y/DcQSMPpKANYVMQ7fBA=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0/go.mod h1:cpgtDBaqD/6ok/UG0jT15/uKjAY8mRA53diogHBg3UI=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0 h1:5pojmb1U1AogINhN3SurB+zm/nIcusopeBNp42f45QM=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0/go.mod h1:57gTHJSE5S1tqg+EKsLPlTWhpHMsWlVmer+LA926XiA=
-go.opentelemetry.io/otel/metric v1.33.0 h1:r+JOocAyeRVXD8lZpjdQjzMadVZp2M4WmQ+5WtEnklQ=
-go.opentelemetry.io/otel/metric v1.33.0/go.mod h1:L9+Fyctbp6HFTddIxClbQkjtubW6O9QS3Ann/M82u6M=
-go.opentelemetry.io/otel/sdk v1.33.0 h1:iax7M131HuAm9QkZotNHEfstof92xM+N8sr3uHXc2IM=
-go.opentelemetry.io/otel/sdk v1.33.0/go.mod h1:A1Q5oi7/9XaMlIWzPSxLRWOI8nG3FnzHJNbiENQuihM=
-go.opentelemetry.io/otel/sdk/metric v1.31.0 h1:i9hxxLJF/9kkvfHppyLL55aW7iIJz4JjxTeYusH7zMc=
-go.opentelemetry.io/otel/sdk/metric v1.31.0/go.mod h1:CRInTMVvNhUKgSAMbKyTMxqOBC0zgyxzW55lZzX43Y8=
-go.opentelemetry.io/otel/trace v1.33.0 h1:cCJuF7LRjUFso9LPnEAHJDB2pqzp+hbO8eu1qqW2d/s=
-go.opentelemetry.io/otel/trace v1.33.0/go.mod h1:uIcdVUZMpTAmz0tI1z04GoVSezK37CbGV4fr1f2nBck=
+go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0 h1:rgMkmiGfix9vFJDcDi1PK8WEQP4FLQwLDfhp5ZLpFeE=
+go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0/go.mod h1:ijPqXp5P6IRRByFVVg9DY8P5HkxkHE5ARIa+86aXPf4=
+go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY=
+go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 h1:OeNbIYk/2C15ckl7glBlOBp5+WlYsOElzTNmiPW/x60=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0/go.mod h1:7Bept48yIeqxP2OZ9/AqIpYS94h2or0aB4FypJTc8ZM=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 h1:tgJ0uaNS4c98WRNUEx5U3aDlrDOI5Rs+1Vifcw4DJ8U=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0/go.mod h1:U7HYyW0zt/a9x5J1Kjs+r1f/d4ZHnYFclhYY2+YbeoE=
+go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ=
+go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE=
+go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A=
+go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU=
+go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU=
+go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ=
+go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k=
+go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE=
 go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4=
 go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4=
 go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
@@ -215,7 +216,6 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
 golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
@@ -242,19 +242,19 @@ google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
 google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
 google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
 google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
-google.golang.org/genproto/googleapis/api v0.0.0-20250102185135-69823020774d h1:H8tOf8XM88HvKqLTxe755haY6r1fqqzLbEnfrmLXlSA=
-google.golang.org/genproto/googleapis/api v0.0.0-20250102185135-69823020774d/go.mod h1:2v7Z7gP2ZUOGsaFyxATQSRoBnKygqVq2Cwnvom7QiqY=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d h1:xJJRGY7TJcvIlpSrN3K6LAWgNFUILlO+OMAqtg9aqnw=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d/go.mod h1:3ENsm/5D1mzDyhpzeRi1NR784I0BcofWBoSc5QqqMK4=
+google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f h1:gap6+3Gk41EItBuyi4XX/bp4oqJ3UwuIMl25yGinuAA=
+google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:Ic02D47M+zbarjYYUlK57y316f2MoN0gjAwI3f2S95o=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f h1:OxYkA3wjPsZyBylwymxSHa7ViiW1Sml4ToBrncvFehI=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:+2Yz8+CLJbIfL9z73EW45avw8Lmge3xVElCP9zEKi50=
 google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
 google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
 google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
 google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
-google.golang.org/grpc v1.69.4 h1:MF5TftSMkd8GLw/m0KM6V8CMOCY6NZ1NQDPGFgbTt4A=
-google.golang.org/grpc v1.69.4/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4=
-google.golang.org/protobuf v1.36.2 h1:R8FeyR1/eLmkutZOM5CWghmo5itiG9z0ktFlTVLuTmU=
-google.golang.org/protobuf v1.36.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
+google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ=
+google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw=
+google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM=
+google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
diff --git a/tests/integration/clientv3/cluster_test.go b/tests/integration/clientv3/cluster_test.go
index 7de4d0ff8..70edd7aad 100644
--- a/tests/integration/clientv3/cluster_test.go
+++ b/tests/integration/clientv3/cluster_test.go
@@ -383,7 +383,7 @@ func TestMaxLearnerInCluster(t *testing.T) {
 	integration2.BeforeTest(t, integration2.WithFailpoint("raftBeforeAdvance", `sleep(100)`))
 
 	// 1. start with a cluster with 3 voting member and max learner 2
-	clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 3, ExperimentalMaxLearners: 2, DisableStrictReconfigCheck: true})
+	clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 3, MaxLearners: 2, DisableStrictReconfigCheck: true})
 	defer clus.Terminate(t)
 
 	// 2. adding 2 learner members should succeed
diff --git a/tests/integration/clientv3/examples/example_metrics_test.go b/tests/integration/clientv3/examples/example_metrics_test.go
index d21c6d393..75b47e53d 100644
--- a/tests/integration/clientv3/examples/example_metrics_test.go
+++ b/tests/integration/clientv3/examples/example_metrics_test.go
@@ -23,10 +23,12 @@ import (
 	"net/http"
 	"strings"
 
-	grpcprom "github.com/grpc-ecosystem/go-grpc-prometheus"
+	grpcprom "github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus"
+	"github.com/prometheus/client_golang/prometheus"
 	"github.com/prometheus/client_golang/prometheus/promhttp"
-	clientv3 "go.etcd.io/etcd/client/v3"
 	"google.golang.org/grpc"
+
+	clientv3 "go.etcd.io/etcd/client/v3"
 )
 
 func mockClient_metrics() {
@@ -35,11 +37,13 @@ func mockClient_metrics() {
 
 func ExampleClient_metrics() {
 	forUnitTestsRunInMockedContext(mockClient_metrics, func() {
+		clientMetrics := grpcprom.NewClientMetrics()
+		prometheus.Register(clientMetrics)
 		cli, err := clientv3.New(clientv3.Config{
 			Endpoints: exampleEndpoints(),
 			DialOptions: []grpc.DialOption{
-				grpc.WithUnaryInterceptor(grpcprom.UnaryClientInterceptor),
-				grpc.WithStreamInterceptor(grpcprom.StreamClientInterceptor),
+				grpc.WithUnaryInterceptor(clientMetrics.UnaryClientInterceptor()),
+				grpc.WithStreamInterceptor(clientMetrics.StreamClientInterceptor()),
 			},
 		})
 		if err != nil {
diff --git a/tests/integration/clientv3/metrics_test.go b/tests/integration/clientv3/metrics_test.go
index 081a2fc0b..3188276c9 100644
--- a/tests/integration/clientv3/metrics_test.go
+++ b/tests/integration/clientv3/metrics_test.go
@@ -16,6 +16,7 @@ package clientv3test
 
 import (
 	"bufio"
+	"bytes"
 	"context"
 	"errors"
 	"io"
@@ -26,7 +27,8 @@ import (
 	"testing"
 	"time"
 
-	grpcprom "github.com/grpc-ecosystem/go-grpc-prometheus"
+	grpcprom "github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus"
+	"github.com/prometheus/client_golang/prometheus"
 	"github.com/prometheus/client_golang/prometheus/promhttp"
 	"google.golang.org/grpc"
 
@@ -73,11 +75,14 @@ func TestV3ClientMetrics(t *testing.T) {
 	clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
 	defer clus.Terminate(t)
 
+	clientMetrics := grpcprom.NewClientMetrics()
+	prometheus.Register(clientMetrics)
+
 	cfg := clientv3.Config{
 		Endpoints: []string{clus.Members[0].GRPCURL},
 		DialOptions: []grpc.DialOption{
-			grpc.WithUnaryInterceptor(grpcprom.UnaryClientInterceptor),
-			grpc.WithStreamInterceptor(grpcprom.StreamClientInterceptor),
+			grpc.WithUnaryInterceptor(clientMetrics.UnaryClientInterceptor()),
+			grpc.WithStreamInterceptor(clientMetrics.StreamClientInterceptor()),
 		},
 	}
 	cli, cerr := integration2.NewClient(t, cfg)
@@ -145,6 +150,24 @@ func sumCountersForMetricAndLabels(t *testing.T, url string, metricName string,
 }
 
 func getHTTPBodyAsLines(t *testing.T, url string) []string {
+	data := getHTTPBodyAsBytes(t, url)
+
+	reader := bufio.NewReader(bytes.NewReader(data))
+	var lines []string
+	for {
+		line, err := reader.ReadString('\n')
+		if err != nil {
+			if errors.Is(err, io.EOF) {
+				break
+			}
+			t.Fatalf("error reading: %v", err)
+		}
+		lines = append(lines, line)
+	}
+	return lines
+}
+
+func getHTTPBodyAsBytes(t *testing.T, url string) []byte {
 	cfgtls := transport.TLSInfo{}
 	tr, err := transport.NewTransport(cfgtls, time.Second)
 	if err != nil {
@@ -160,20 +183,10 @@ func getHTTPBodyAsLines(t *testing.T, url string) []string {
 	if err != nil {
 		t.Fatalf("Error fetching: %v", err)
 	}
-
-	reader := bufio.NewReader(resp.Body)
-	var lines []string
-	for {
-		line, err := reader.ReadString('\n')
-		if err != nil {
-			if errors.Is(err, io.EOF) {
-				break
-			} else {
-				t.Fatalf("error reading: %v", err)
-			}
-		}
-		lines = append(lines, line)
+	defer resp.Body.Close()
+	body, err := io.ReadAll(resp.Body)
+	if err != nil {
+		t.Fatalf("Error reading http body: %v", err)
 	}
-	resp.Body.Close()
-	return lines
+	return body
 }
diff --git a/tests/integration/embed/embed_proxy_test.go b/tests/integration/embed/embed_proxy_test.go
index 50f2a175f..6d20e1802 100644
--- a/tests/integration/embed/embed_proxy_test.go
+++ b/tests/integration/embed/embed_proxy_test.go
@@ -14,6 +14,6 @@
 
 //go:build cluster_proxy
 
-// The purpose of this (empty) package is too keep following test working:
+// Package embed_test is an empty package that exists to keep the following test working:
 // #  go test -tags=cluster_proxy ./integration/embed
 package embed_test
diff --git a/tests/integration/utl_wal_version_test.go b/tests/integration/utl_wal_version_test.go
index 33e1b0aec..7540d71eb 100644
--- a/tests/integration/utl_wal_version_test.go
+++ b/tests/integration/utl_wal_version_test.go
@@ -85,7 +85,7 @@ func TestEtcdVersionFromWAL(t *testing.T) {
 	if err != nil {
 		t.Fatal(err)
 	}
-	assert.Equal(t, &semver.Version{Major: 3, Minor: 6}, walVersion.MinimalEtcdVersion())
+	assert.Equal(t, &semver.Version{Major: 3, Minor: 5}, walVersion.MinimalEtcdVersion())
 }
 
 func waitForClusterVersionReady(srv *embed.Etcd) error {
diff --git a/tests/integration/v3_alarm_test.go b/tests/integration/v3_alarm_test.go
index 50a701e68..44403b02d 100644
--- a/tests/integration/v3_alarm_test.go
+++ b/tests/integration/v3_alarm_test.go
@@ -247,11 +247,10 @@ func TestV3CorruptAlarm(t *testing.T) {
 	for i := 0; i < 5; i++ {
 		presp, perr := clus.Client(0).Put(context.TODO(), "abc", "aaa")
 		if perr != nil {
-			if !eqErrGRPC(perr, rpctypes.ErrCorrupt) {
-				t.Fatalf("expected %v, got %+v (%v)", rpctypes.ErrCorrupt, presp, perr)
-			} else {
+			if eqErrGRPC(perr, rpctypes.ErrCorrupt) {
 				return
 			}
+			t.Fatalf("expected %v, got %+v (%v)", rpctypes.ErrCorrupt, presp, perr)
 		}
 		time.Sleep(time.Second)
 	}
@@ -347,10 +346,9 @@ func TestV3CorruptAlarmWithLeaseCorrupted(t *testing.T) {
 	time.Sleep(time.Second)
 	presp, perr := clus.Client(0).Put(context.TODO(), "abc", "aaa")
 	if perr != nil {
-		if !eqErrGRPC(perr, rpctypes.ErrCorrupt) {
-			t.Fatalf("expected %v, got %+v (%v)", rpctypes.ErrCorrupt, presp, perr)
-		} else {
+		if eqErrGRPC(perr, rpctypes.ErrCorrupt) {
 			return
 		}
+		t.Fatalf("expected %v, got %+v (%v)", rpctypes.ErrCorrupt, presp, perr)
 	}
 }
diff --git a/tests/integration/v3_failover_test.go b/tests/integration/v3_failover_test.go
index b4ce09e1a..79ad25a4d 100644
--- a/tests/integration/v3_failover_test.go
+++ b/tests/integration/v3_failover_test.go
@@ -124,9 +124,8 @@ func putWithRetries(t *testing.T, cli *clientv3.Client, key, val string, retryCo
 			retryCount--
 			if shouldRetry(err) {
 				continue
-			} else {
-				t.Fatal(err)
 			}
+			t.Fatal(err)
 		}
 		break
 	}
@@ -156,9 +155,8 @@ func getWithRetries(t *testing.T, cli *clientv3.Client, key, val string, retryCo
 			retryCount--
 			if shouldRetry(err) {
 				continue
-			} else {
-				t.Fatal(err)
 			}
+			t.Fatal(err)
 		}
 		break
 	}
diff --git a/tests/robustness/client/client.go b/tests/robustness/client/client.go
index 531fbacba..fa13c7910 100644
--- a/tests/robustness/client/client.go
+++ b/tests/robustness/client/client.go
@@ -356,6 +356,7 @@ func toWatchEvent(event clientv3.Event) (watch model.WatchEvent) {
 		watch.PrevValue = &model.ValueRevision{
 			Value:       model.ToValueOrHash(string(event.PrevKv.Value)),
 			ModRevision: event.PrevKv.ModRevision,
+			Version:     event.PrevKv.Version,
 		}
 	}
 	watch.IsCreate = event.IsCreate()
diff --git a/tests/robustness/failpoint/trigger.go b/tests/robustness/failpoint/trigger.go
index 0f863fb54..9ae9a8084 100644
--- a/tests/robustness/failpoint/trigger.go
+++ b/tests/robustness/failpoint/trigger.go
@@ -75,7 +75,7 @@ func (t triggerCompact) Trigger(ctx context.Context, _ *testing.T, member e2e.Et
 		}
 		rev = resp.Header.Revision
 
-		if !t.multiBatchCompaction || rev > int64(clus.Cfg.ServerConfig.CompactionBatchLimit) {
+		if !t.multiBatchCompaction || rev > int64(clus.Cfg.ServerConfig.ExperimentalCompactionBatchLimit) {
 			break
 		}
 		time.Sleep(50 * time.Millisecond)
@@ -99,7 +99,7 @@ func (t triggerCompact) Available(config e2e.EtcdProcessClusterConfig, _ e2e.Etc
 	// For multiBatchCompaction we need to guarantee that there are enough revisions between two compaction requests.
 	// With addition of compaction requests to traffic this might be hard if experimental-compaction-batch-limit is too high.
 	if t.multiBatchCompaction {
-		return config.ServerConfig.CompactionBatchLimit <= 10
+		return config.ServerConfig.ExperimentalCompactionBatchLimit <= 10
 	}
 	return true
 }
diff --git a/tests/robustness/main_test.go b/tests/robustness/main_test.go
index 11da8bdcd..de45d943d 100644
--- a/tests/robustness/main_test.go
+++ b/tests/robustness/main_test.go
@@ -46,7 +46,6 @@ var (
 )
 
 func TestMain(m *testing.M) {
-	rand.Seed(time.Now().UnixNano())
 	testRunner.TestMain(m)
 }
 
diff --git a/tests/robustness/model/describe.go b/tests/robustness/model/describe.go
index 71f2b15f2..43eb58c06 100644
--- a/tests/robustness/model/describe.go
+++ b/tests/robustness/model/describe.go
@@ -91,6 +91,9 @@ func describeGuaranteedTxn(txn *TxnRequest) string {
 		if txn.Conditions[0].Key != txn.OperationsOnSuccess[0].Put.Key || (len(txn.OperationsOnFailure) == 1 && txn.Conditions[0].Key != txn.OperationsOnFailure[0].Range.Start) {
 			return ""
 		}
+		if txn.Conditions[0].ExpectedVersion > 0 {
+			return ""
+		}
 		if txn.Conditions[0].ExpectedRevision == 0 {
 			return fmt.Sprintf("guaranteedCreate(%q, %s)", txn.Conditions[0].Key, describeValueOrHash(txn.OperationsOnSuccess[0].Put.Value))
 		}
@@ -106,8 +109,12 @@ func describeGuaranteedTxn(txn *TxnRequest) string {
 
 func describeEtcdConditions(conds []EtcdCondition) string {
 	opsDescription := make([]string, len(conds))
-	for i := range conds {
-		opsDescription[i] = fmt.Sprintf("mod_rev(%s)==%d", conds[i].Key, conds[i].ExpectedRevision)
+	for i, cond := range conds {
+		if cond.ExpectedVersion > 0 {
+			opsDescription[i] = fmt.Sprintf("ver(%s)==%d", cond.Key, cond.ExpectedVersion)
+		} else {
+			opsDescription[i] = fmt.Sprintf("mod_rev(%s)==%d", cond.Key, cond.ExpectedRevision)
+		}
 	}
 	return strings.Join(opsDescription, " && ")
 }
diff --git a/tests/robustness/model/deterministic.go b/tests/robustness/model/deterministic.go
index 6f570ce08..9b61d13af 100644
--- a/tests/robustness/model/deterministic.go
+++ b/tests/robustness/model/deterministic.go
@@ -127,7 +127,13 @@ func (s EtcdState) Step(request EtcdRequest) (EtcdState, MaybeEtcdResponse) {
 	case Txn:
 		failure := false
 		for _, cond := range request.Txn.Conditions {
-			if val := newState.KeyValues[cond.Key]; val.ModRevision != cond.ExpectedRevision {
+			val := newState.KeyValues[cond.Key]
+			if cond.ExpectedVersion > 0 {
+				if val.Version != cond.ExpectedVersion {
+					failure = true
+					break
+				}
+			} else if val.ModRevision != cond.ExpectedRevision {
 				failure = true
 				break
 			}
@@ -149,9 +155,14 @@ func (s EtcdState) Step(request EtcdRequest) (EtcdState, MaybeEtcdResponse) {
 				if op.Put.LeaseID != 0 && !leaseExists {
 					break
 				}
+				ver := int64(1)
+				if val, exists := newState.KeyValues[op.Put.Key]; exists && val.Version > 0 {
+					ver = val.Version + 1
+				}
 				newState.KeyValues[op.Put.Key] = ValueRevision{
 					Value:       op.Put.Value,
 					ModRevision: newState.Revision + 1,
+					Version:     ver,
 				}
 				increaseRevision = true
 				newState = detachFromOldLease(newState, op.Put.Key)
@@ -326,6 +337,7 @@ type TxnRequest struct {
 type EtcdCondition struct {
 	Key              string
 	ExpectedRevision int64
+	ExpectedVersion  int64
 }
 
 type EtcdOperation struct {
@@ -434,6 +446,7 @@ func (el EtcdLease) DeepCopy() EtcdLease {
 type ValueRevision struct {
 	Value       ValueOrHash
 	ModRevision int64
+	Version     int64
 }
 
 type ValueOrHash struct {
diff --git a/tests/robustness/model/deterministic_test.go b/tests/robustness/model/deterministic_test.go
index 5e7bc0f05..6e7512623 100644
--- a/tests/robustness/model/deterministic_test.go
+++ b/tests/robustness/model/deterministic_test.go
@@ -82,8 +82,8 @@ var commonTestScenarios = []modelTestCase{
 		operations: []testOperation{
 			{req: putRequest("key1", "1"), resp: putResponse(2)},
 			{req: putRequest("key2", "2"), resp: putResponse(3)},
-			{req: listRequest("key", 0), resp: rangeResponse([]*mvccpb.KeyValue{{Key: []byte("key1"), Value: []byte("1"), ModRevision: 2}, {Key: []byte("key2"), Value: []byte("2"), ModRevision: 3}}, 2, 3)},
-			{req: listRequest("key", 0), resp: rangeResponse([]*mvccpb.KeyValue{{Key: []byte("key1"), Value: []byte("1"), ModRevision: 2}, {Key: []byte("key2"), Value: []byte("2"), ModRevision: 3}}, 2, 3)},
+			{req: listRequest("key", 0), resp: rangeResponse([]*mvccpb.KeyValue{{Key: []byte("key1"), Value: []byte("1"), ModRevision: 2, Version: 1}, {Key: []byte("key2"), Value: []byte("2"), ModRevision: 3, Version: 1}}, 2, 3)},
+			{req: listRequest("key", 0), resp: rangeResponse([]*mvccpb.KeyValue{{Key: []byte("key1"), Value: []byte("1"), ModRevision: 2, Version: 1}, {Key: []byte("key2"), Value: []byte("2"), ModRevision: 3, Version: 1}}, 2, 3)},
 		},
 	},
 	{
@@ -93,26 +93,26 @@ var commonTestScenarios = []modelTestCase{
 			{req: putRequest("key2", "2"), resp: putResponse(3)},
 			{req: putRequest("key3", "3"), resp: putResponse(4)},
 			{req: listRequest("key", 0), resp: rangeResponse([]*mvccpb.KeyValue{
-				{Key: []byte("key1"), Value: []byte("1"), ModRevision: 2},
-				{Key: []byte("key2"), Value: []byte("2"), ModRevision: 3},
-				{Key: []byte("key3"), Value: []byte("3"), ModRevision: 4},
+				{Key: []byte("key1"), Value: []byte("1"), ModRevision: 2, Version: 1},
+				{Key: []byte("key2"), Value: []byte("2"), ModRevision: 3, Version: 1},
+				{Key: []byte("key3"), Value: []byte("3"), ModRevision: 4, Version: 1},
 			}, 3, 4)},
 			{req: listRequest("key", 4), resp: rangeResponse([]*mvccpb.KeyValue{
-				{Key: []byte("key1"), Value: []byte("1"), ModRevision: 2},
-				{Key: []byte("key2"), Value: []byte("2"), ModRevision: 3},
-				{Key: []byte("key3"), Value: []byte("3"), ModRevision: 4},
+				{Key: []byte("key1"), Value: []byte("1"), ModRevision: 2, Version: 1},
+				{Key: []byte("key2"), Value: []byte("2"), ModRevision: 3, Version: 1},
+				{Key: []byte("key3"), Value: []byte("3"), ModRevision: 4, Version: 1},
 			}, 3, 4)},
 			{req: listRequest("key", 3), resp: rangeResponse([]*mvccpb.KeyValue{
-				{Key: []byte("key1"), Value: []byte("1"), ModRevision: 2},
-				{Key: []byte("key2"), Value: []byte("2"), ModRevision: 3},
-				{Key: []byte("key3"), Value: []byte("3"), ModRevision: 4},
+				{Key: []byte("key1"), Value: []byte("1"), ModRevision: 2, Version: 1},
+				{Key: []byte("key2"), Value: []byte("2"), ModRevision: 3, Version: 1},
+				{Key: []byte("key3"), Value: []byte("3"), ModRevision: 4, Version: 1},
 			}, 3, 4)},
 			{req: listRequest("key", 2), resp: rangeResponse([]*mvccpb.KeyValue{
-				{Key: []byte("key1"), Value: []byte("1"), ModRevision: 2},
-				{Key: []byte("key2"), Value: []byte("2"), ModRevision: 3},
+				{Key: []byte("key1"), Value: []byte("1"), ModRevision: 2, Version: 1},
+				{Key: []byte("key2"), Value: []byte("2"), ModRevision: 3, Version: 1},
 			}, 3, 4)},
 			{req: listRequest("key", 1), resp: rangeResponse([]*mvccpb.KeyValue{
-				{Key: []byte("key1"), Value: []byte("1"), ModRevision: 2},
+				{Key: []byte("key1"), Value: []byte("1"), ModRevision: 2, Version: 1},
 			}, 3, 4)},
 		},
 	},
@@ -123,19 +123,19 @@ var commonTestScenarios = []modelTestCase{
 			{req: putRequest("key2", "1"), resp: putResponse(3)},
 			{req: putRequest("key1", "2"), resp: putResponse(4)},
 			{req: listRequest("key", 0), resp: rangeResponse([]*mvccpb.KeyValue{
-				{Key: []byte("key1"), Value: []byte("2"), ModRevision: 4},
-				{Key: []byte("key2"), Value: []byte("1"), ModRevision: 3},
-				{Key: []byte("key3"), Value: []byte("3"), ModRevision: 2},
+				{Key: []byte("key1"), Value: []byte("2"), ModRevision: 4, Version: 1},
+				{Key: []byte("key2"), Value: []byte("1"), ModRevision: 3, Version: 1},
+				{Key: []byte("key3"), Value: []byte("3"), ModRevision: 2, Version: 1},
 			}, 3, 4)},
 			{req: listRequest("key", 0), resp: rangeResponse([]*mvccpb.KeyValue{
-				{Key: []byte("key2"), Value: []byte("1"), ModRevision: 3},
-				{Key: []byte("key1"), Value: []byte("2"), ModRevision: 4},
-				{Key: []byte("key3"), Value: []byte("3"), ModRevision: 2},
+				{Key: []byte("key2"), Value: []byte("1"), ModRevision: 3, Version: 1},
+				{Key: []byte("key1"), Value: []byte("2"), ModRevision: 4, Version: 1},
+				{Key: []byte("key3"), Value: []byte("3"), ModRevision: 2, Version: 1},
 			}, 3, 4), expectFailure: true},
 			{req: listRequest("key", 0), resp: rangeResponse([]*mvccpb.KeyValue{
-				{Key: []byte("key3"), Value: []byte("3"), ModRevision: 2},
-				{Key: []byte("key2"), Value: []byte("1"), ModRevision: 3},
-				{Key: []byte("key1"), Value: []byte("2"), ModRevision: 4},
+				{Key: []byte("key3"), Value: []byte("3"), ModRevision: 2, Version: 1},
+				{Key: []byte("key2"), Value: []byte("1"), ModRevision: 3, Version: 1},
+				{Key: []byte("key1"), Value: []byte("2"), ModRevision: 4, Version: 1},
 			}, 3, 4), expectFailure: true},
 		},
 	},
@@ -146,7 +146,7 @@ var commonTestScenarios = []modelTestCase{
 			{req: getRequest("key"), resp: getResponse("key", "123456789012345678901", 2, 2), expectFailure: true},
 			{req: getRequest("key"), resp: getResponse("key", "012345678901234567890", 2, 2)},
 			{req: putRequest("key", "123456789012345678901"), resp: putResponse(3)},
-			{req: getRequest("key"), resp: getResponse("key", "123456789012345678901", 3, 3)},
+			{req: getRequest("key"), resp: getResponseWithVer("key", "123456789012345678901", 3, 2, 3)},
 			{req: getRequest("key"), resp: getResponse("key", "012345678901234567890", 3, 3), expectFailure: true},
 		},
 	},
@@ -228,8 +228,8 @@ var commonTestScenarios = []modelTestCase{
 			{req: getRequest("key"), resp: getResponse("key", "1", 2, 2), expectFailure: true},
 			{req: getRequest("key"), resp: getResponse("key", "1", 2, 3), expectFailure: true},
 			{req: getRequest("key"), resp: getResponse("key", "1", 3, 3), expectFailure: true},
-			{req: getRequest("key"), resp: getResponse("key", "2", 2, 2), expectFailure: true},
-			{req: getRequest("key"), resp: getResponse("key", "2", 3, 3)},
+			{req: getRequest("key"), resp: getResponseWithVer("key", "2", 2, 2, 2), expectFailure: true},
+			{req: getRequest("key"), resp: getResponseWithVer("key", "2", 3, 2, 3)},
 		},
 	},
 	{
@@ -239,7 +239,7 @@ var commonTestScenarios = []modelTestCase{
 			{req: compareRevisionAndPutRequest("key1", 0, "2"), resp: compareRevisionAndPutResponse(true, 2)},
 			{req: compareRevisionAndPutRequest("key1", 0, "3"), resp: compareRevisionAndPutResponse(true, 3), expectFailure: true},
 			{req: txnRequestSingleOperation(compareRevision("key1", 0), putOperation("key1", "4"), putOperation("key1", "5")), resp: txnPutResponse(false, 3)},
-			{req: getRequest("key1"), resp: getResponse("key1", "5", 3, 3)},
+			{req: getRequest("key1"), resp: getResponseWithVer("key1", "5", 3, 2, 3)},
 			{req: compareRevisionAndPutRequest("key2", 0, "6"), resp: compareRevisionAndPutResponse(true, 4)},
 		},
 	},
@@ -291,7 +291,7 @@ var commonTestScenarios = []modelTestCase{
 			{req: putWithLeaseRequest("key", "2", 1), resp: putResponse(2)},
 			{req: putRequest("key", "3"), resp: putResponse(3)},
 			{req: leaseRevokeRequest(1), resp: leaseRevokeResponse(3)},
-			{req: getRequest("key"), resp: getResponse("key", "3", 3, 3)},
+			{req: getRequest("key"), resp: getResponseWithVer("key", "3", 3, 2, 3)},
 		},
 	},
 	{
@@ -302,7 +302,7 @@ var commonTestScenarios = []modelTestCase{
 			{req: putWithLeaseRequest("key", "2", 1), resp: putResponse(2)},
 			{req: putWithLeaseRequest("key", "3", 2), resp: putResponse(3)},
 			{req: leaseRevokeRequest(1), resp: leaseRevokeResponse(3)},
-			{req: getRequest("key"), resp: getResponse("key", "3", 3, 3)},
+			{req: getRequest("key"), resp: getResponseWithVer("key", "3", 3, 2, 3)},
 			{req: leaseRevokeRequest(2), resp: leaseRevokeResponse(4)},
 			{req: getRequest("key"), resp: emptyGetResponse(4)},
 		},
@@ -313,7 +313,7 @@ var commonTestScenarios = []modelTestCase{
 			{req: leaseGrantRequest(1), resp: leaseGrantResponse(1)},
 			{req: putWithLeaseRequest("key", "2", 1), resp: putResponse(2)},
 			{req: putWithLeaseRequest("key", "3", 1), resp: putResponse(3)},
-			{req: getRequest("key"), resp: getResponse("key", "3", 3, 3)},
+			{req: getRequest("key"), resp: getResponseWithVer("key", "3", 3, 2, 3)},
 		},
 	},
 	{
diff --git a/tests/robustness/model/history.go b/tests/robustness/model/history.go
index 20ebd1c7c..fc224a8af 100644
--- a/tests/robustness/model/history.go
+++ b/tests/robustness/model/history.go
@@ -183,12 +183,13 @@ func toEtcdCondition(cmp clientv3.Cmp) (cond EtcdCondition) {
 	switch {
 	case cmp.Result == etcdserverpb.Compare_EQUAL && cmp.Target == etcdserverpb.Compare_MOD:
 		cond.Key = string(cmp.KeyBytes())
-	case cmp.Result == etcdserverpb.Compare_EQUAL && cmp.Target == etcdserverpb.Compare_CREATE:
+		cond.ExpectedRevision = cmp.TargetUnion.(*etcdserverpb.Compare_ModRevision).ModRevision
+	case cmp.Result == etcdserverpb.Compare_EQUAL && cmp.Target == etcdserverpb.Compare_VERSION:
+		cond.ExpectedVersion = cmp.TargetUnion.(*etcdserverpb.Compare_Version).Version
 		cond.Key = string(cmp.KeyBytes())
 	default:
 		panic(fmt.Sprintf("Compare not supported, target: %q, result: %q", cmp.Target, cmp.Result))
 	}
-	cond.ExpectedRevision = cmp.TargetUnion.(*etcdserverpb.Compare_ModRevision).ModRevision
 	return cond
 }
 
@@ -228,6 +229,7 @@ func toEtcdOperationResult(resp *etcdserverpb.ResponseOp) EtcdOperationResult {
 				ValueRevision: ValueRevision{
 					Value:       ToValueOrHash(string(kv.Value)),
 					ModRevision: kv.ModRevision,
+					Version:     kv.Version,
 				},
 			}
 		}
@@ -342,7 +344,11 @@ func emptyGetResponse(revision int64) MaybeEtcdResponse {
 }
 
 func getResponse(key, value string, modRevision, revision int64) MaybeEtcdResponse {
-	return rangeResponse([]*mvccpb.KeyValue{{Key: []byte(key), Value: []byte(value), ModRevision: modRevision}}, 1, revision)
+	return getResponseWithVer(key, value, modRevision, 1, revision)
+}
+
+func getResponseWithVer(key, value string, modRevision, ver, revision int64) MaybeEtcdResponse {
+	return rangeResponse([]*mvccpb.KeyValue{{Key: []byte(key), Value: []byte(value), ModRevision: modRevision, Version: ver}}, 1, revision)
 }
 
 func rangeResponse(kvs []*mvccpb.KeyValue, count int64, revision int64) MaybeEtcdResponse {
@@ -354,6 +360,7 @@ func rangeResponse(kvs []*mvccpb.KeyValue, count int64, revision int64) MaybeEtc
 			ValueRevision: ValueRevision{
 				Value:       ToValueOrHash(string(kv.Value)),
 				ModRevision: kv.ModRevision,
+				Version:     kv.Version,
 			},
 		}
 	}
diff --git a/tests/robustness/model/non_deterministic_test.go b/tests/robustness/model/non_deterministic_test.go
index d5c981973..b6622e6fa 100644
--- a/tests/robustness/model/non_deterministic_test.go
+++ b/tests/robustness/model/non_deterministic_test.go
@@ -33,7 +33,7 @@ func TestModelNonDeterministic(t *testing.T) {
 			operations: []testOperation{
 				{req: putRequest("key1", "1"), resp: failedResponse(errors.New("failed"))},
 				{req: putRequest("key2", "2"), resp: putResponse(3)},
-				{req: listRequest("key", 0), resp: rangeResponse([]*mvccpb.KeyValue{{Key: []byte("key1"), Value: []byte("1"), ModRevision: 2}, {Key: []byte("key2"), Value: []byte("2"), ModRevision: 3}}, 2, 3)},
+				{req: listRequest("key", 0), resp: rangeResponse([]*mvccpb.KeyValue{{Key: []byte("key1"), Value: []byte("1"), ModRevision: 2, Version: 1}, {Key: []byte("key2"), Value: []byte("2"), ModRevision: 3, Version: 1}}, 2, 3)},
 			},
 		},
 		{
@@ -41,7 +41,7 @@ func TestModelNonDeterministic(t *testing.T) {
 			operations: []testOperation{
 				{req: putRequest("key1", "1"), resp: failedResponse(errors.New("failed"))},
 				{req: putRequest("key2", "2"), resp: putResponse(2)},
-				{req: listRequest("key", 0), resp: rangeResponse([]*mvccpb.KeyValue{{Key: []byte("key2"), Value: []byte("2"), ModRevision: 2}}, 1, 2)},
+				{req: listRequest("key", 0), resp: rangeResponse([]*mvccpb.KeyValue{{Key: []byte("key2"), Value: []byte("2"), ModRevision: 2, Version: 1}}, 1, 2)},
 			},
 		},
 		{
@@ -90,14 +90,14 @@ func TestModelNonDeterministic(t *testing.T) {
 				// One failed request, one persisted.
 				{req: putRequest("key", "1"), resp: putResponse(2)},
 				{req: putRequest("key", "2"), resp: failedResponse(errors.New("failed"))},
-				{req: getRequest("key"), resp: getResponse("key", "3", 3, 3), expectFailure: true},
-				{req: getRequest("key"), resp: getResponse("key", "3", 2, 3), expectFailure: true},
-				{req: getRequest("key"), resp: getResponse("key", "2", 2, 2), expectFailure: true},
-				{req: getRequest("key"), resp: getResponse("key", "2", 3, 3)},
+				{req: getRequest("key"), resp: getResponseWithVer("key", "3", 3, 2, 3), expectFailure: true},
+				{req: getRequest("key"), resp: getResponseWithVer("key", "3", 2, 2, 3), expectFailure: true},
+				{req: getRequest("key"), resp: getResponseWithVer("key", "2", 2, 2, 2), expectFailure: true},
+				{req: getRequest("key"), resp: getResponseWithVer("key", "2", 3, 2, 3)},
 				// Two failed request, two persisted.
 				{req: putRequest("key", "3"), resp: failedResponse(errors.New("failed"))},
 				{req: putRequest("key", "4"), resp: failedResponse(errors.New("failed"))},
-				{req: getRequest("key"), resp: getResponse("key", "4", 5, 5)},
+				{req: getRequest("key"), resp: getResponseWithVer("key", "4", 5, 4, 5)},
 			},
 		},
 		{
@@ -133,7 +133,7 @@ func TestModelNonDeterministic(t *testing.T) {
 				{req: putRequest("key", "4"), resp: putResponse(4)},
 				{req: compareRevisionAndPutRequest("key", 5, ""), resp: compareRevisionAndPutResponse(false, 4)},
 				{req: putRequest("key", "5"), resp: failedResponse(errors.New("failed"))},
-				{req: getRequest("key"), resp: getResponse("key", "5", 5, 5)},
+				{req: getRequest("key"), resp: getResponseWithVer("key", "5", 5, 4, 5)},
 			},
 		},
 		{
@@ -249,13 +249,13 @@ func TestModelNonDeterministic(t *testing.T) {
 				// One failed request, one persisted.
 				{req: putRequest("key", "1"), resp: putResponse(2)},
 				{req: compareRevisionAndPutRequest("key", 2, "2"), resp: failedResponse(errors.New("failed"))},
-				{req: getRequest("key"), resp: getResponse("key", "2", 2, 2), expectFailure: true},
-				{req: getRequest("key"), resp: getResponse("key", "2", 3, 3)},
+				{req: getRequest("key"), resp: getResponseWithVer("key", "2", 2, 2, 2), expectFailure: true},
+				{req: getRequest("key"), resp: getResponseWithVer("key", "2", 3, 2, 3)},
 				// Two failed request, two persisted.
 				{req: putRequest("key", "3"), resp: putResponse(4)},
 				{req: compareRevisionAndPutRequest("key", 4, "4"), resp: failedResponse(errors.New("failed"))},
 				{req: compareRevisionAndPutRequest("key", 5, "5"), resp: failedResponse(errors.New("failed"))},
-				{req: getRequest("key"), resp: getResponse("key", "5", 6, 6)},
+				{req: getRequest("key"), resp: getResponseWithVer("key", "5", 6, 5, 6)},
 			},
 		},
 		{
diff --git a/tests/robustness/options/server_config_options.go b/tests/robustness/options/server_config_options.go
index 99fe2acef..ade51592c 100644
--- a/tests/robustness/options/server_config_options.go
+++ b/tests/robustness/options/server_config_options.go
@@ -26,9 +26,9 @@ func WithSnapshotCount(input ...uint64) e2e.EPClusterOption {
 	}
 }
 
-func WithCompactionBatchLimit(input ...int) e2e.EPClusterOption {
+func WithExperimentalCompactionBatchLimit(input ...int) e2e.EPClusterOption {
 	return func(c *e2e.EtcdProcessClusterConfig) {
-		c.ServerConfig.CompactionBatchLimit = input[internalRand.Intn(len(input))]
+		c.ServerConfig.ExperimentalCompactionBatchLimit = input[internalRand.Intn(len(input))]
 	}
 }
 
diff --git a/tests/robustness/report/wal.go b/tests/robustness/report/wal.go
index 133ba9bb1..077ddc889 100644
--- a/tests/robustness/report/wal.go
+++ b/tests/robustness/report/wal.go
@@ -199,10 +199,20 @@ func parseEntryNormal(ent raftpb.Entry) (*model.EtcdRequest, error) {
 			OperationsOnFailure: []model.EtcdOperation{},
 		}
 		for _, cmp := range raftReq.Txn.Compare {
-			txn.Conditions = append(txn.Conditions, model.EtcdCondition{
-				Key:              string(cmp.Key),
-				ExpectedRevision: cmp.GetModRevision(),
-			})
+			switch {
+			case cmp.Result == pb.Compare_EQUAL && cmp.Target == pb.Compare_VERSION:
+				txn.Conditions = append(txn.Conditions, model.EtcdCondition{
+					Key:             string(cmp.Key),
+					ExpectedVersion: cmp.GetVersion(),
+				})
+			case cmp.Result == pb.Compare_EQUAL && cmp.Target == pb.Compare_MOD:
+				txn.Conditions = append(txn.Conditions, model.EtcdCondition{
+					Key:              string(cmp.Key),
+					ExpectedRevision: cmp.GetModRevision(),
+				})
+			default:
+				panic(fmt.Sprintf("unsupported condition: %+v", cmp))
+			}
 		}
 		for _, op := range raftReq.Txn.Success {
 			txn.OperationsOnSuccess = append(txn.OperationsOnSuccess, toEtcdOperation(op))
diff --git a/tests/robustness/scenarios/scenarios.go b/tests/robustness/scenarios/scenarios.go
index c9c27a751..0c4aac2fb 100644
--- a/tests/robustness/scenarios/scenarios.go
+++ b/tests/robustness/scenarios/scenarios.go
@@ -97,8 +97,8 @@ func Exploratory(_ *testing.T) []TestScenario {
 		options.WithSubsetOptions(randomizableOptions...),
 		e2e.WithGoFailEnabled(true),
 		// Set low minimal compaction batch limit to allow for triggering multi batch compaction failpoints.
-		options.WithCompactionBatchLimit(10, 100, 1000),
-		e2e.WithWatchProcessNotifyInterval(100 * time.Millisecond),
+		options.WithExperimentalCompactionBatchLimit(10, 100, 1000),
+		e2e.WithExperimentalWatchProcessNotifyInterval(100 * time.Millisecond),
 	}
 
 	if e2e.CouldSetSnapshotCatchupEntries(e2e.BinPath.Etcd) {
@@ -219,7 +219,7 @@ func Regression(t *testing.T) []TestScenario {
 		Traffic:   traffic.Kubernetes,
 		Cluster: *e2e.NewConfig(
 			e2e.WithClusterSize(1),
-			e2e.WithCompactionBatchLimit(300),
+			e2e.WithExperimentalCompactionBatchLimit(300),
 			e2e.WithSnapshotCount(1000),
 			e2e.WithGoFailEnabled(true),
 		),
diff --git a/tests/robustness/traffic/etcd.go b/tests/robustness/traffic/etcd.go
index c426064f6..906f3d2cb 100644
--- a/tests/robustness/traffic/etcd.go
+++ b/tests/robustness/traffic/etcd.go
@@ -18,6 +18,7 @@ import (
 	"context"
 	"fmt"
 	"math/rand"
+	"time"
 
 	"golang.org/x/time/rate"
 
@@ -108,7 +109,7 @@ func (t etcdTraffic) Name() string {
 	return "Etcd"
 }
 
-func (t etcdTraffic) Run(ctx context.Context, c *client.RecordingClient, limiter *rate.Limiter, ids identity.Provider, lm identity.LeaseIDStorage, nonUniqueWriteLimiter ConcurrencyLimiter, finish <-chan struct{}) {
+func (t etcdTraffic) RunTrafficLoop(ctx context.Context, c *client.RecordingClient, limiter *rate.Limiter, ids identity.Provider, lm identity.LeaseIDStorage, nonUniqueWriteLimiter ConcurrencyLimiter, finish <-chan struct{}) {
 	lastOperationSucceeded := true
 	var lastRev int64
 	var requestType etcdRequestType
@@ -155,6 +156,35 @@ func (t etcdTraffic) Run(ctx context.Context, c *client.RecordingClient, limiter
 	}
 }
 
+func (t etcdTraffic) RunCompactLoop(ctx context.Context, c *client.RecordingClient, period time.Duration, finish <-chan struct{}) {
+	var lastRev int64 = 2
+	ticker := time.NewTicker(period)
+	defer ticker.Stop()
+	for {
+		select {
+		case <-ctx.Done():
+			return
+		case <-finish:
+			return
+		case <-ticker.C:
+		}
+		statusCtx, cancel := context.WithTimeout(ctx, RequestTimeout)
+		resp, err := c.Status(statusCtx, c.Endpoints()[0])
+		cancel()
+		if err != nil {
+			continue
+		}
+
+		// Range allows for both revision has been compacted and future revision errors
+		compactRev := random.RandRange(lastRev, resp.Header.Revision+5)
+		_, err = c.Compact(ctx, compactRev)
+		if err != nil {
+			continue
+		}
+		lastRev = compactRev
+	}
+}
+
 func filterOutNonUniqueEtcdWrites(choices []random.ChoiceWeight[etcdRequestType]) (resp []random.ChoiceWeight[etcdRequestType]) {
 	for _, choice := range choices {
 		if choice.Choice != Delete && choice.Choice != LeaseRevoke {
diff --git a/tests/robustness/traffic/kubernetes.go b/tests/robustness/traffic/kubernetes.go
index 445f75324..56962368a 100644
--- a/tests/robustness/traffic/kubernetes.go
+++ b/tests/robustness/traffic/kubernetes.go
@@ -19,7 +19,9 @@ import (
 	"errors"
 	"fmt"
 	"math/rand"
+	"strconv"
 	"sync"
+	"time"
 
 	"golang.org/x/sync/errgroup"
 	"golang.org/x/time/rate"
@@ -56,7 +58,7 @@ func (t kubernetesTraffic) ExpectUniqueRevision() bool {
 	return true
 }
 
-func (t kubernetesTraffic) Run(ctx context.Context, c *client.RecordingClient, limiter *rate.Limiter, ids identity.Provider, lm identity.LeaseIDStorage, nonUniqueWriteLimiter ConcurrencyLimiter, finish <-chan struct{}) {
+func (t kubernetesTraffic) RunTrafficLoop(ctx context.Context, c *client.RecordingClient, limiter *rate.Limiter, ids identity.Provider, lm identity.LeaseIDStorage, nonUniqueWriteLimiter ConcurrencyLimiter, finish <-chan struct{}) {
 	kc := kubernetes.Client{Client: &clientv3.Client{KV: c}}
 	s := newStorage()
 	keyPrefix := "/registry/" + t.resource + "/"
@@ -127,7 +129,7 @@ func (t kubernetesTraffic) Read(ctx context.Context, kc kubernetes.Interface, s
 		}
 		hasMore = resp.Count > int64(len(resp.Kvs))
 		if hasMore {
-			cont = string(kvs[len(kvs)-1].Key)
+			cont = string(kvs[len(kvs)-1].Key) + "\x00"
 		}
 	}
 	s.Reset(revision, kvs)
@@ -205,6 +207,60 @@ func (t kubernetesTraffic) generateKey() string {
 	return fmt.Sprintf("/registry/%s/%s/%s", t.resource, t.namespace, stringutil.RandString(5))
 }
 
+func (t kubernetesTraffic) RunCompactLoop(ctx context.Context, c *client.RecordingClient, interval time.Duration, finish <-chan struct{}) {
+	// Based on https://github.com/kubernetes/apiserver/blob/7dd4904f1896e11244ba3c5a59797697709de6b6/pkg/storage/etcd3/compact.go#L112-L127
+	var compactTime int64
+	var rev int64
+	var err error
+	for {
+		select {
+		case <-time.After(interval):
+		case <-ctx.Done():
+			return
+		case <-finish:
+			return
+		}
+
+		compactTime, rev, err = compact(ctx, c, compactTime, rev)
+		if err != nil {
+			continue
+		}
+	}
+}
+
+// Based on https://github.com/kubernetes/apiserver/blob/7dd4904f1896e11244ba3c5a59797697709de6b6/pkg/storage/etcd3/compact.go#L30
+const (
+	compactRevKey = "compact_rev_key"
+)
+
+func compact(ctx context.Context, client *client.RecordingClient, t, rev int64) (int64, int64, error) {
+	// Based on https://github.com/kubernetes/apiserver/blob/7dd4904f1896e11244ba3c5a59797697709de6b6/pkg/storage/etcd3/compact.go#L133-L162
+	resp, err := client.Txn(ctx).
+		If(clientv3.Compare(clientv3.Version(compactRevKey), "=", t)).
+		Then(clientv3.OpPut(compactRevKey, strconv.FormatInt(rev, 10))).
+		Else(clientv3.OpGet(compactRevKey)).
+		Commit()
+	if err != nil {
+		return t, rev, err
+	}
+
+	curRev := resp.Header.Revision
+
+	if !resp.Succeeded {
+		curTime := resp.Responses[0].GetResponseRange().Kvs[0].Version
+		return curTime, curRev, nil
+	}
+	curTime := t + 1
+
+	if rev == 0 {
+		return curTime, curRev, nil
+	}
+	if _, err = client.Compact(ctx, rev); err != nil {
+		return curTime, curRev, err
+	}
+	return curTime, curRev, nil
+}
+
 type KubernetesRequestType string
 
 const (
diff --git a/tests/robustness/traffic/traffic.go b/tests/robustness/traffic/traffic.go
index 91f130705..d86663dff 100644
--- a/tests/robustness/traffic/traffic.go
+++ b/tests/robustness/traffic/traffic.go
@@ -28,7 +28,6 @@ import (
 	"go.etcd.io/etcd/tests/v3/robustness/client"
 	"go.etcd.io/etcd/tests/v3/robustness/identity"
 	"go.etcd.io/etcd/tests/v3/robustness/model"
-	"go.etcd.io/etcd/tests/v3/robustness/random"
 	"go.etcd.io/etcd/tests/v3/robustness/report"
 )
 
@@ -81,7 +80,7 @@ func SimulateTraffic(ctx context.Context, t *testing.T, lg *zap.Logger, clus *e2
 			defer wg.Done()
 			defer c.Close()
 
-			traffic.Run(ctx, c, limiter, ids, lm, nonUniqueWriteLimiter, finish)
+			traffic.RunTrafficLoop(ctx, c, limiter, ids, lm, nonUniqueWriteLimiter, finish)
 			mux.Lock()
 			reports = append(reports, c.Report())
 			mux.Unlock()
@@ -101,7 +100,8 @@ func SimulateTraffic(ctx context.Context, t *testing.T, lg *zap.Logger, clus *e2
 			if profile.CompactPeriod != time.Duration(0) {
 				compactionPeriod = profile.CompactPeriod
 			}
-			RunCompactLoop(ctx, c, compactionPeriod, finish)
+
+			traffic.RunCompactLoop(ctx, c, compactionPeriod, finish)
 			mux.Lock()
 			reports = append(reports, c.Report())
 			mux.Unlock()
@@ -195,35 +195,7 @@ func (p Profile) WithCompactionPeriod(cp time.Duration) Profile {
 }
 
 type Traffic interface {
-	Run(ctx context.Context, c *client.RecordingClient, qpsLimiter *rate.Limiter, ids identity.Provider, lm identity.LeaseIDStorage, nonUniqueWriteLimiter ConcurrencyLimiter, finish <-chan struct{})
+	RunTrafficLoop(ctx context.Context, c *client.RecordingClient, qpsLimiter *rate.Limiter, ids identity.Provider, lm identity.LeaseIDStorage, nonUniqueWriteLimiter ConcurrencyLimiter, finish <-chan struct{})
+	RunCompactLoop(ctx context.Context, c *client.RecordingClient, period time.Duration, finish <-chan struct{})
 	ExpectUniqueRevision() bool
 }
-
-func RunCompactLoop(ctx context.Context, c *client.RecordingClient, period time.Duration, finish <-chan struct{}) {
-	var lastRev int64 = 2
-	timer := time.NewTimer(period)
-	for {
-		timer.Reset(period)
-		select {
-		case <-ctx.Done():
-			return
-		case <-finish:
-			return
-		case <-timer.C:
-		}
-		statusCtx, cancel := context.WithTimeout(ctx, RequestTimeout)
-		resp, err := c.Status(statusCtx, c.Endpoints()[0])
-		cancel()
-		if err != nil {
-			continue
-		}
-
-		// Range allows for both revision has been compacted and future revision errors
-		compactRev := random.RandRange(lastRev, resp.Header.Revision+5)
-		_, err = c.Compact(ctx, compactRev)
-		if err != nil {
-			continue
-		}
-		lastRev = compactRev
-	}
-}
diff --git a/tests/robustness/validate/operations_test.go b/tests/robustness/validate/operations_test.go
index c6b7afab9..a244d9f75 100644
--- a/tests/robustness/validate/operations_test.go
+++ b/tests/robustness/validate/operations_test.go
@@ -290,6 +290,7 @@ func keyValueRevision(key, value string, rev int64) model.KeyValue {
 		ValueRevision: model.ValueRevision{
 			Value:       model.ToValueOrHash(value),
 			ModRevision: rev,
+			Version:     1,
 		},
 	}
 }
diff --git a/tests/robustness/validate/patch_history.go b/tests/robustness/validate/patch_history.go
index 4c6d4794f..5ec3bd2ae 100644
--- a/tests/robustness/validate/patch_history.go
+++ b/tests/robustness/validate/patch_history.go
@@ -108,12 +108,11 @@ func patchOperations(operations []porcupine.Operation, watchRevision, putReturnT
 			if !persisted {
 				// Remove non persisted operations
 				continue
+			}
+			if txnRevision != 0 {
+				op.Output = model.MaybeEtcdResponse{Persisted: true, PersistedRevision: txnRevision}
 			} else {
-				if txnRevision != 0 {
-					op.Output = model.MaybeEtcdResponse{Persisted: true, PersistedRevision: txnRevision}
-				} else {
-					op.Output = model.MaybeEtcdResponse{Persisted: true}
-				}
+				op.Output = model.MaybeEtcdResponse{Persisted: true}
 			}
 		}
 		// Leave operation as it is as we cannot discard it.
diff --git a/tests/robustness/validate/validate_test.go b/tests/robustness/validate/validate_test.go
index 14a3a3210..e847af672 100644
--- a/tests/robustness/validate/validate_test.go
+++ b/tests/robustness/validate/validate_test.go
@@ -1633,8 +1633,8 @@ func TestValidateWatch(t *testing.T) {
 								{
 									Events: []model.WatchEvent{
 										putWatchEvent("a", "1", 2, true),
-										putWatchEventWithPrevKV("a", "2", 3, false, "1", 2),
-										deleteWatchEventWithPrevKV("a", 4, "2", 3),
+										putWatchEventWithPrevKVV("a", "2", 3, false, "1", 2, 1),
+										deleteWatchEventWithPrevKVV("a", 4, "2", 3, 2),
 										putWatchEvent("a", "4", 5, true),
 									},
 								},
@@ -1864,21 +1864,31 @@ func deleteWatchEvent(key string, rev int64) model.WatchEvent {
 }
 
 func putWatchEventWithPrevKV(key, value string, rev int64, isCreate bool, prevValue string, modRev int64) model.WatchEvent {
+	return putWatchEventWithPrevKVV(key, value, rev, isCreate, prevValue, modRev, 0)
+}
+
+func putWatchEventWithPrevKVV(key, value string, rev int64, isCreate bool, prevValue string, modRev, ver int64) model.WatchEvent {
 	return model.WatchEvent{
 		PersistedEvent: putPersistedEvent(key, value, rev, isCreate),
 		PrevValue: &model.ValueRevision{
 			Value:       model.ToValueOrHash(prevValue),
 			ModRevision: modRev,
+			Version:     ver,
 		},
 	}
 }
 
 func deleteWatchEventWithPrevKV(key string, rev int64, prevValue string, modRev int64) model.WatchEvent {
+	return deleteWatchEventWithPrevKVV(key, rev, prevValue, modRev, 0)
+}
+
+func deleteWatchEventWithPrevKVV(key string, rev int64, prevValue string, modRev, ver int64) model.WatchEvent {
 	return model.WatchEvent{
 		PersistedEvent: deletePersistedEvent(key, rev),
 		PrevValue: &model.ValueRevision{
 			Value:       model.ToValueOrHash(prevValue),
 			ModRevision: modRev,
+			Version:     ver,
 		},
 	}
 }
diff --git a/tools/local-tester/bridge/bridge.go b/tools/local-tester/bridge/bridge.go
index 25ca7e015..d20bff4c9 100644
--- a/tools/local-tester/bridge/bridge.go
+++ b/tools/local-tester/bridge/bridge.go
@@ -13,6 +13,8 @@
 // limitations under the License.
 
 // Package main is the entry point for the local tester network bridge.
+// Deprecated: etcd local tester is now deprecated. Use the etcd robustness
+// testing suite instead to validate etcd behaviour under failure conditions.
 package main
 
 import (
diff --git a/tools/local-tester/bridge/dispatch.go b/tools/local-tester/bridge/dispatch.go
index 2aae79db6..07ee5fb26 100644
--- a/tools/local-tester/bridge/dispatch.go
+++ b/tools/local-tester/bridge/dispatch.go
@@ -12,6 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+// Deprecated: etcd local tester is now deprecated. Use the etcd robustness
+// testing suite instead to validate etcd behaviour under failure conditions.
 package main
 
 import (