{"url":"https://api.github.com/repos/vitessio/vitess/releases/401046514","assets_url":"https://api.github.com/repos/vitessio/vitess/releases/401046514/assets","upload_url":"https://uploads.github.com/repos/vitessio/vitess/releases/401046514/assets{?name,label}","html_url":"https://github.com/vitessio/vitess/releases/tag/v24.0.4","id":401046514,"author":{"login":"arthurschreiber","id":2195,"node_id":"MDQ6VXNlcjIxOTU=","avatar_url":"https://avatars.githubusercontent.com/u/2195?v=4","gravatar_id":"","url":"https://api.github.com/users/arthurschreiber","html_url":"https://github.com/arthurschreiber","followers_url":"https://api.github.com/users/arthurschreiber/followers","following_url":"https://api.github.com/users/arthurschreiber/following{/other_user}","gists_url":"https://api.github.com/users/arthurschreiber/gists{/gist_id}","starred_url":"https://api.github.com/users/arthurschreiber/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/arthurschreiber/subscriptions","organizations_url":"https://api.github.com/users/arthurschreiber/orgs","repos_url":"https://api.github.com/users/arthurschreiber/repos","events_url":"https://api.github.com/users/arthurschreiber/events{/privacy}","received_events_url":"https://api.github.com/users/arthurschreiber/received_events","type":"User","user_view_type":"public","site_admin":false},"node_id":"RE_kwDOAKf4z84X53vy","tag_name":"v24.0.4","target_commitish":"967ff0b9222d950b20cb47a36109c0e1a9dce507","name":"Vitess v24.0.4","draft":false,"immutable":false,"prerelease":false,"created_at":"2026-10-01T14:24:56Z","updated_at":"2026-10-01T14:47:28Z","published_at":"2026-10-01T14:25:14Z","assets":[{"url":"https://api.github.com/repos/vitessio/vitess/releases/assets/603457317","id":603457317,"node_id":"RA_kwDOAKf4z84j-Acl","name":"vitess-24.0.4-967ff0b.tar.gz","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/gzip","state":"uploaded","size":802443912,"digest":"sha256:ae5cb5cb217a8a338d5c139bc1d99cbc4bf20609ab4cde48be68811a831d8f6d","download_count":601,"created_at":"2026-10-01T14:39:03Z","updated_at":"2026-10-01T14:39:36Z","browser_download_url":"https://github.com/vitessio/vitess/releases/download/v24.0.4/vitess-24.0.4-967ff0b.tar.gz"},{"url":"https://api.github.com/repos/vitessio/vitess/releases/assets/603457335","id":603457335,"node_id":"RA_kwDOAKf4z84j-Ac3","name":"vitess-24.0.4-967ff0b.x86_64.rpm","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-redhat-package-manager","state":"uploaded","size":800083157,"digest":"sha256:b8d36a76afa017262bb20b9216984949abfe614f232b5386460d90f560a21287","download_count":18,"created_at":"2026-10-01T14:39:03Z","updated_at":"2026-10-01T14:39:29Z","browser_download_url":"https://github.com/vitessio/vitess/releases/download/v24.0.4/vitess-24.0.4-967ff0b.x86_64.rpm"},{"url":"https://api.github.com/repos/vitessio/vitess/releases/assets/603457272","id":603457272,"node_id":"RA_kwDOAKf4z84j-Ab4","name":"vitess_24.0.4-967ff0b_amd64.deb","label":"","uploader":{"login":"github-actions[bot]","id":41898282,"node_id":"MDM6Qm90NDE4OTgyODI=","avatar_url":"https://avatars.githubusercontent.com/in/15368?v=4","gravatar_id":"","url":"https://api.github.com/users/github-actions%5Bbot%5D","html_url":"https://github.com/apps/github-actions","followers_url":"https://api.github.com/users/github-actions%5Bbot%5D/followers","following_url":"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/github-actions%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/github-actions%5Bbot%5D/repos","events_url":"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/github-actions%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false},"content_type":"application/x-debian-package","state":"uploaded","size":799561352,"digest":"sha256:c9f335499a586c2d8f0f34db2f70bf6932b57c48b8ad525245f4d36890553d30","download_count":29,"created_at":"2026-10-01T14:39:01Z","updated_at":"2026-10-01T14:39:28Z","browser_download_url":"https://github.com/vitessio/vitess/releases/download/v24.0.4/vitess_24.0.4-967ff0b_amd64.deb"}],"tarball_url":"https://api.github.com/repos/vitessio/vitess/tarball/v24.0.4","zipball_url":"https://api.github.com/repos/vitessio/vitess/zipball/v24.0.4","body":"# Release of Vitess v24.0.4\r\n\r\n## Summary\r\n\r\n### lz4 backup engine: library upgrade and `--compression-level` mapping\r\n\r\nThe `lz4` compression engine now uses the `pierrec/lz4/v4` library instead of `pierrec/lz4` v2. This fixes broken block decoding on amd64 in the v2 library. The frame format is unchanged, so backups written by older Vitess versions remain restorable and backups written by this version are restorable by older versions.\r\n\r\nThe upgrade changes how `--compression-level` is interpreted for the lz4 engine. Values `0` and `1`, including the default of `1`, select the fast compressor; `1` previously requested a hash-chain search depth of 1. Values `2` through `9` now select lz4's named hash-chain levels (`Level2` through `Level9`) instead of using the raw value as the hash-chain search depth, so higher values produce a better ratio at more CPU cost. Negative values, which previously requested an unlimited search, and values above `9`, which were used as the search depth, select `Level9`. Other compression engines are not affected.\r\n\r\nSee [#20778](https://github.com/vitessio/vitess/pull/20778) for details.\r\n\r\n### Connections whose certificate revocation cannot be checked against a configured CRL are rejected\r\n\r\nThe certificate revocation lists configured with `--grpc-crl`, `--mysql-server-ssl-crl`, `--tablet-grpc-crl`, `--vtgate-grpc-crl`, and the other `*-crl` flags are now enforced in every SSL mode and on resumed TLS sessions, see [GHSA-fxqj-c35w-x6rq](https://github.com/vitessio/vitess/security/advisories/GHSA-fxqj-c35w-x6rq). Each certificate of the peer's verified chain, except the trust anchor the chain ends at, is checked against the CRLs signed by its issuer, the next certificate of the chain.\r\n\r\n#### Connections that are now rejected\r\n\r\nIn the `preferred` and `required` SSL modes, the server's certificate chain is otherwise not verified. A client with a CRL now builds that chain to the configured CA, or to the system roots when no CA is configured, and rejects the connection when the chain cannot be built or verified, rather than leave the CRL unchecked. For example:\r\n\r\n- A client in `required` mode with a CRL but no CA file, connecting to a server whose certificate a private CA issued.\r\n- A client with a CRL and the root CA configured, connecting to a server that presents its certificate without the intermediate CA that issued it.\r\n- A client with a CRL connecting to a server whose certificate has expired or is not valid for server authentication, which these modes accept without a CRL.\r\n\r\nTo keep such connections working, configure the CA that the server's chain leads to, have the server present its whole chain and a valid certificate, or drop the CRL.\r\n\r\nA gRPC client with a CRL configured (`--tablet-grpc-crl`, `--vtgate-grpc-crl`, `--vtctld-grpc-crl`, and the other `*-grpc-crl` flags) but neither a client certificate nor a CA now connects with TLS, verifying the server against the system roots, rather than in plaintext with the CRL silently ignored. Configure the CA along with the CRL, or drop the CRL.\r\n\r\n#### Configurations that are refused at startup\r\n\r\nThe following configurations are now refused when the TLS configuration is built, at startup, because the CRL cannot be applied as configured. Most of them used to run with the CRL silently ignored.\r\n\r\n- A server-side CRL (`--grpc-crl`, `--mysql-server-ssl-crl`) without the matching CA (`--grpc-ca`, `--mysql-server-ssl-ca`). Without a CA no client certificate is requested, so the CRL could not apply. Configure the CA, or drop the CRL.\r\n- A server-side CRL (`--grpc-crl`, `--mysql-server-ssl-crl`) without the matching certificate and key (`--grpc-cert` and `--grpc-key`, `--mysql-server-ssl-cert` and `--mysql-server-ssl-key`). The server is then not configured for TLS at all: the gRPC server used to start in plaintext, and the MySQL server without TLS, with the CRL silently ignored. Configure the certificate and the key along with the CA, or drop the CRL.\r\n- A `*-crl` file that holds no CRL. Point the flag at a file with at least one `X509 CRL` block, or drop the flag.\r\n- A CRL that the certificate of its issuer in the CA file does not validate: its signature does not verify against that certificate, or that certificate lacks the `cRLSign` key usage. Re-issue the CRL, or the CA certificate with `cRLSign`. When such an issuer is only found in a peer's chain, as an intermediate CA the peer presents, that peer's connections are rejected instead.\r\n- A CRL whose authority key identifier names a different key than its issuing CA certificate's, although that certificate's key signed it. It would otherwise be passed over. Re-issue it with the certificate's subject key identifier as its authority key identifier.\r\n- A CRL signed with an algorithm that is not supported.\r\n- A CRL whose issuer name is encoded differently from the subject of the CA certificate in the CA file that signed it, as a CRL written by another tool than the CA's can be. The names are matched byte for byte, so the CRL would not be applied. Re-issue the CRL with the certificate's subject as its issuer.\r\n- A delta CRL, an indirect CRL, or a CRL that its issuing distribution point limits to end-entity certificates, to CA certificates, to attribute certificates, or to some revocation reasons: only complete CRLs are supported. A CRL that names its distribution point without limiting itself otherwise is accepted, and every such partition of an issuer's CRL is applied.\r\n- A CRL that carries a critical extension other than the issuing distribution point, on the list or on an entry.\r\n- A CRL whose `thisUpdate` lies more than five minutes in the future, so that a CRL staged ahead of time cannot supersede the current one. Provide the current CRL, and check the clocks.\r\n\r\n#### Other changes to how CRLs are applied\r\n\r\n- A peer passes when any of its verified chains holds no revoked certificate, as verification itself accepts the peer on any valid chain; every chain used to have to be clean. An intermediate CA cross-signed by two roots and revoked by one of them, as during a CA rollover, still carries the peer through the other root.\r\n- A trust anchor, that is, a CA certificate in the CA file that a chain ends at, is trusted as configured and not checked against its issuer's CRL. The exception is an anchor whose issuer is in the CA file as well and whose issuer's CRL lists it, or an anchor issued, through CA certificates in the CA file, under a CA certificate listed that way. Every chain that ends at such an anchor is rejected, whether or not the peer presents it, and a warning names the CA certificate when the configuration is built. An intermediate CA configured without its root is not checked against the root's CRL.\r\n- When a CRL file holds several complete CRLs from one issuer, only the newest applies, as a complete CRL supersedes the ones issued before it; they used to be combined.\r\n- A CRL only applies when the certificate of its issuer in the chain validates it. A CA that signs its CRLs with a separate certificate under the same name is therefore not supported.\r\n- A CRL whose authority key identifier names a different key than the CA certificate's, as the CRL of a re-keyed CA's predecessor does, is not held against that CA's certificates.\r\n\r\nSee [#21054](https://github.com/vitessio/vitess/pull/21054) and [#21153](https://github.com/vitessio/vitess/pull/21153) for details.\r\n\r\n### Table ACL: statements whose tables cannot be determined are denied under strict table ACL\r\n\r\nUnder strict table ACL (`--queryserver-config-strict-table-acl`), vttablet checks a statement against the tables its planner derives for it. `DO`, `CALL`, `REPAIR`, `OPTIMIZE` and `LOAD DATA` are parsed into nodes that discard their table-bearing text, so no permission was derived for them and the check had nothing to enforce: any authenticated caller could run them against tables the ACL denies, with vttablet's own MySQL privileges — a `DO` carrying a table-reading subquery or a `CALL` into a procedure body to read, and a server-side `LOAD DATA INFILE` to write. See [GHSA-w6mx-2f8x-pqf4](https://github.com/vitessio/vitess/security/advisories/GHSA-w6mx-2f8x-pqf4).\r\n\r\nvttablet now fails closed: when it cannot determine a statement's tables, it denies the statement under strict table ACL rather than skip the check. With strict table ACL on, these five statements are denied for every caller outside the exempt ACL (`--queryserver-config-acl-exempt-acl`), including callers whose table grants would otherwise have sufficed, since the tablet cannot confirm which tables the statement touches. Operators who need them should issue them as a caller in the exempt ACL. With dry-run (`--queryserver-config-enable-table-acl-dry-run`) the denial is only recorded and the statement runs. Nothing changes with strict table ACL off.\r\n\r\nThese denials have no table to name, so they are counted under a new `TableName` label, `undetermined-table-set` (with an empty `TableGroup`), in `TableACLDenied`. With dry-run on, every non-exempt `DO`, `CALL`, `REPAIR`, `OPTIMIZE` and `LOAD DATA` from a request carrying a caller id increments `TableACLPseudoDenied` under that label whether or not strict table ACL is on, so operators sizing a strict-ACL rollout will see a new series appear.\r\n\r\nThe exported `planbuilder.BuildPermissions` now returns a second result, `tablesUndetermined bool`, alongside the permissions. This breaks any out-of-tree caller on purpose: a one-result compatibility wrapper would keep returning \"no permissions\" for exactly these statements with no way to learn the table set was undetermined, so a caller left on it would silently keep the behavior this fix closes. Callers should take the new result and deny the statement when it is true.\r\n\r\nThis covers statements. A stored **function** invoked inside an expression (`SELECT f()`, a `WHERE` clause, a `SET` in DML) is not `CALL`ed, so it still runs its body with vttablet's MySQL privileges while the ACL checks only the tables the statement itself names; Vitess does not parse `CREATE FUNCTION`, so this applies to functions defined directly in MySQL. That gap is tracked in [#21134](https://github.com/vitessio/vitess/issues/21134).\r\n\r\nSee [#21053](https://github.com/vitessio/vitess/pull/21053) for details.\r\n\r\n### Table ACL: reads embedded in DDL, `EXPLAIN`, `SHOW`, `SET`, and queries against `dual` are checked under strict table ACL\r\n\r\nThis completes the fix for [GHSA-w6mx-2f8x-pqf4](https://github.com/vitessio/vitess/security/advisories/GHSA-w6mx-2f8x-pqf4) begun [above](#table-acl-statements-whose-tables-cannot-be-determined-are-denied-under-strict-table-acl): that change fails closed on statements whose tables the parser discards; this one derives permissions for reads embedded in statements the planner does parse but never checked, so a caller with no grant on a table could read it through them. Each read is now checked like a plain `SELECT` of the same tables, under strict table ACL (`--queryserver-config-strict-table-acl`), with the same dry-run and exempt-ACL behavior as any other table ACL check:\r\n\r\n- `CREATE TABLE ... AS SELECT` requires `READER` on the tables the `SELECT` reads (through CTEs, joins and unions included), in addition to `ADMIN` on the table it creates. `CREATE VIEW ... AS SELECT` and `ALTER VIEW ... AS SELECT` require the same `READER` on their source tables: a view reads nothing when it is defined, but it reads its sources as the tablet's MySQL user whenever it is queried, and the ACL then sees only the view's name, so the source is checked when the view is defined, as MySQL requires `SELECT` on it.\r\n- `EXPLAIN`, in any format, and `DESCRIBE <statement>` now require the explained statement's permissions, `WRITER` on the target of a DML included, as MySQL requires the explained statement's privileges. `EXPLAIN ANALYZE` executes the statement, and a plain `EXPLAIN` reads too: MySQL reads single-row tables and evaluates uncorrelated subqueries while it optimizes, and the plan shows the outcome (`Impossible WHERE`), so an `EXPLAIN` answers a yes/no question about the data.\r\n- `SHOW ... WHERE <expr>` requires `READER` on the tables read by any subquery in the filter, which MySQL evaluates. The `SHOW`'s own subject (the table of `SHOW COLUMNS FROM t`) remains unchecked. This covers `SHOW VITESS_MIGRATIONS ... WHERE` as well.\r\n- `SET` requires `READER` on the tables read by any subquery in its expressions.\r\n- A query against `dual` requires `READER` on the tables read by any subquery in it, such as `SELECT (SELECT v FROM t) FROM dual`. vttablet exempted a query against `dual` from the table ACL entirely, subqueries included; only a query that reads nothing but `dual` is exempt now.\r\n\r\nA `CREATE TABLE` that vttablet's parser cannot fully parse is forwarded to MySQL as the client's raw text, with only the `CREATE TABLE <name>` prefix known to the planner. Some such statements copy rows from a table the planner never sees (`CREATE TABLE t (SELECT ...)`, `CREATE TABLE t AS TABLE src`, an `EXCEPT` or `INTERSECT` source), and the planner cannot tell them from a valid statement in syntax Vitess lacks. Every partially parsed `CREATE TABLE` is therefore treated as a statement whose tables cannot be determined and denied the same way, for callers outside the exempt ACL; a `CREATE TABLE` in syntax vttablet does not parse must be issued by a caller in the exempt ACL. Parsing these sources is tracked in [#21138](https://github.com/vitessio/vitess/issues/21138).\r\n\r\nA statement flagged this way now has the permissions the planner did derive checked first, so a caller lacking `ADMIN` on the table a partial `CREATE TABLE` creates is denied on that table by name, and a dry run records both that denial and the undetermined one.\r\n\r\nConnection settings — the SET statements vtgate attaches to a session's queries, and the pre-queries of a reservation — are applied to a connection with no table ACL check. Under strict table ACL, vttablet now rejects a setting whose expressions contain a subquery, and a reservation pre-query it cannot parse as a `SET`: settings carry constants, and vtgate only sends values. Without strict table ACL every setting is accepted as before, since there is nothing for the check to protect. With table ACL dry run (`--queryserver-config-enable-table-acl-dry-run`), the setting is accepted as well, as dry run lets through any request the table ACL would deny, and vttablet logs a throttled warning naming the setting that strict table ACL would reject (with `--sanitize-log-messages`, only the variables it sets). So that a targeted session's settings are constants too, vtgate now evaluates a targeted `SET` of a system variable before storing it; see [below](#vtgate-a-targeted-sessions-set-of-a-system-variable-is-evaluated-once).\r\n\r\nSee [#21139](https://github.com/vitessio/vitess/pull/21139) for details.\r\n\r\n### VTGate: a targeted session's `SET` of a system variable is evaluated once\r\n\r\nA `SET` of a system variable in a session targeted at a shard (`use ks:-80`) is now evaluated once on the target shard, with the tablet's table ACL checking any read, and the session applies and stores the resulting value, as an untargeted session does. Previously such a session stored the expression as written and re-evaluated it on every reserved connection. As a result:\r\n\r\n- `SELECT @@var` after a non-constant targeted `SET` now returns the value instead of failing to evaluate the stored text.\r\n- A targeted `SET` that fails no longer leaves its value in the session.\r\n- Each targeted `SET` costs one additional round trip to the shard.\r\n\r\n**Compatibility note:** a vtgate without this change (before v23.0.7 or v24.0.4) still stores a targeted session's `SET` expression as written. Against a v24.0.4 vttablet running strict table ACL without dry run, a session on such a vtgate that runs `SET @@var = (<subquery>)` while targeted has that setting rejected on every later query until the client reconnects. In the usual upgrade order, vttablets before vtgates, this applies for as long as the upgrade lasts: avoid subqueries in targeted `SET` statements until every vtgate is upgraded, or upgrade vtgates first. Without strict table ACL nothing changes for such a session.\r\n\r\nSee [#21139](https://github.com/vitessio/vitess/pull/21139) for details.\r\n\r\n### Legacy vtctld HTTP API removed\r\n\r\nThe HTTP API that vtctld served under `/api/` has been removed because it was dead code that exposed a security attack surface. It was built for the vtctld web UI, which VTAdmin replaced in v16. Nothing has served or called it since, and VTAdmin reaches vtctld over gRPC. What remained was an unauthenticated HTTP surface that served topology data, tablet health, arbitrary vtctl commands, schema changes, and keyspace and shard validations. Its `--security-policy` coverage varied from one endpoint to the next. Removing it removes that surface, rather than patching it endpoint by endpoint.\r\n\r\nThe removed endpoints are `cells`, `keyspaces`, `keyspace`, `shards`, `srv_keyspace`, `tablets`, `tablet_statuses`, `tablet_health`, `topology_info`, `topodata`, `vtctl`, `schema/apply`, and `features`, and the keyspace, shard, and tablet action endpoints behind them. The `--cell`, `--proxy-tablets`, `--action-timeout`, and `--tablet-health-keep-alive` flags of vtctld and vtcombo that configured the API are now deprecated no-ops, so a process started with them still starts. They will be removed in v26.\r\n\r\n**Migration**: use `vtctldclient`, or the `VtctldServer` gRPC service it calls, for anything a script did against `/api/`. Remove `--cell`, `--proxy-tablets`, `--action-timeout`, and `--tablet-health-keep-alive` from vtctld and vtcombo startup arguments. The `/debug/health` and `/debug/status` endpoints are unchanged.\r\n\r\n**Impact**: requests to `/api/` on vtctld's HTTP port return `404 Not Found`. Passing one of the four flags logs a deprecation warning and has no effect. For anyone who builds on the Go packages, `vtctld.InitVtctld`, `vtctld.ActionRepository`, `vtctld.ActionResult`, and `vtctld.TabletWithURL` are gone.\r\n\r\nSee [#21171](https://github.com/vitessio/vitess/pull/21171) for the removal and [#21170](https://github.com/vitessio/vitess/issues/21170) for the removal of the flags in v26.\r\n\r\n### Optional gRPC TLS: connections are counted by transport\r\n\r\nA gRPC server started with `--grpc-enable-optional-tls` now reports its connections by transport, `tls` or `plaintext`, in two new stats: `GrpcOptionalTlsOpenConnections`, the connections currently open, and `GrpcOptionalTlsConnections`, the connections handshaken so far. Optional TLS serves plain-text connections unauthenticated so that clients can be moved to TLS one at a time, including when `--grpc-ca` is set, whose client certificate check only applies to the TLS connections. The stats are the evidence to check before dropping `--grpc-enable-optional-tls`: the first shows whether a plain-text client is connected right now, which matters because gRPC connections are long-lived and a client that connected long ago does not handshake again, and the second whether any has connected lately. Neither shows a client that is offline or connects only now and then, so they support the decision rather than prove it. A server that has both flags also says so in its startup warning now.\r\n\r\nSee [#21162](https://github.com/vitessio/vitess/pull/21162) for details.\r\n\r\n------------\r\nThe entire changelog for this release can be found [here](https://github.com/vitessio/vitess/blob/main/changelog/24.0/24.0.4/changelog.md).\r\n\r\nThe release includes 41 merged Pull Requests.\r\n\r\nThanks to all our contributors: @app/vitess-bot, @arthurschreiber\r\n\r\n","mentions_count":1}