Skip to content

Smart client handoffs: reinstate auto-enlistment for the hosted providers and remove [Experimental] - #3248

Merged
mgravell merged 3 commits into
mainfrom
marc/maint-auto-by-default
Oct 7, 2026
Merged

mgravell merged 3 commits into
mainfrom
marc/maint-auto-by-default

Conversation

@mgravell

@mgravell mgravell commented Sep 18, 2026 •

Copy link
Copy Markdown
Collaborator
  • Reinstates automatic enlistment for the hosted providers, which 3.3.0 shipped with deliberately withheld so
    that maintenance notifications went out purely opt-in.
  • removes the SER010 experimental annotation against smart-client-handoffs APIs

Nothing here is new behaviour: #3191 built and reviewed this, and the overrides were commented out rather than
deleted precisely so that what came back would be exactly what was reviewed. This is that, so it should read as
a revert of the withholding rather than as a change.

What turns on

You connect to Recognized as Notifications
something.cloud.redislabs.com, .cloud.redis.io, .redislabs.com Redis Cloud on (Auto)
something.redis.azure.net, .redisenterprise.cache.azure.net Azure Managed Redis on (Auto)
defaults=enterprise, defaults=rediscloud, defaults=amr named explicitly on (Auto)
anything else nothing off

Auto, not Enabled, throughout: a deployment that cannot deliver has to keep working, so the opt-in is
refused and the feature simply stays off. Enabled means required and rejects such a connection, which is a
choice a caller makes deliberately.

The library default stays Disabled. Only the three hosted providers enlist. OSS Redis, Valkey and Garnet
do not know the subcommand, and an unsolicited error reply on every connection would be a poor first
impression.

Why this is safe to turn on

The evidence from #3191, all against real deployments:

  • Redis Enterprise and Redis Cloud: 26/26 fault-injector scenarios, MOVING handoffs acted on ~16s before the
    server closed the socket, endpoint retirement proven against a real node removal, and the destructive family
    (shard, node and proxy failure) run supervised.
  • Azure Managed Redis, which is the pre-emptive case and therefore the one that needed checking: AMR does not
    emit these yet, so the opt-in is refused with ERR unknown subcommand 'MAINT_NOTIFICATIONS'. Measured on a
    live instance, the connection survives that in sync - PING, ECHO, PING all correct afterwards -
    which matters because the opt-in is pipelined with HELLO, AUTH and the CLIENT commands before any reply
    is read. A server that closed the connection, desynchronised the reply stream, or answered a hopeful +OK
    would have made this default harmful rather than merely inert. AzureManagedRedisOptInTests keeps that
    reproducible, and is written so that the day AMR ships support the same tests report accepted instead.

Changes

  • The three provider overrides return in their original form.
  • PublicAPI.Unshipped.txt regains the three [SER010]override entries; eng/public-api.py --check agrees the
    file is sorted.
  • Docs follow the behaviour back: ServerMaintenanceEvent.md returns to "usually not" with the two-column
    now/will-be table collapsed and the temporary notice dropped, and Configuration.md and exp/SER010.md lose
    their "purely opt-in" paragraphs. The defaults= table row added while the feature was opt-in is kept, since
    it is useful either way.
  • DefaultOptionsTests.MaintenanceNotificationDefaultPerProvider flips its first two rows to Auto, which is
    what it was written to do. The last two stay Disabled, and they are what stops the theory passing merely
    because every host returns the same answer.

MaintenanceOptInClientTests.DefaultIsOff needed no change: it connects to the in-process server on loopback,
which no provider recognizes, so Disabled is still right there.

One thing to decide before merging

main has since promoted geo-redundant failover out of preview, and maintenance notifications are suppressed
inside a multi-group connection - documented as "in development", because how a handoff and a cross-region
failover should agree about who is in charge is not yet settled. With auto-enlistment reinstated, a recognized
Redis Cloud hostname inside a ConnectGroupAsync group gets Auto and has it suppressed, with a warning
logged. That is the designed behaviour, but it becomes reachable without anyone configuring anything, where
before it needed an explicit maintNotifications.

Verification

dotnet build Build.csproj clean across every TFM. Full suite on two cores in Release with CI=true: 6470
passed, 0 failed, 161 skipped.

3.3.0 shipped maintenance notifications purely opt-in, with the provider
overrides commented out rather than deleted so that what came back would be
exactly what was reviewed. This is that, pending the thumbs up.

The three overrides return in their original form: Redis Cloud, Azure Managed
Redis and Redis Enterprise select Auto, so a connection string naming nothing
but a recognized hostname gets the feature. Auto rather than Enabled
throughout, because a deployment that cannot deliver has to keep working - the
opt-in is refused and the feature stays off, rather than the connection being
rejected. The library default stays Disabled: OSS Redis, Valkey and Garnet do
not know the subcommand, and an unsolicited error on every connection would be
a poor first impression.

Docs follow the behaviour back:

- ServerMaintenanceEvent's "do I need to configure anything?" returns to
  "usually not", with the two-column now/will-be table collapsed and the
  temporary notice dropped. Kept the `defaults=` table row that was added while
  the feature was opt-in, since it is useful either way.
- Configuration.md and exp/SER010.md lose their "purely opt-in" paragraphs.
- DefaultOptionsTests.MaintenanceNotificationDefaultPerProvider flips its first
  two rows to Auto, which is what it was written to do. The last two rows stay
  Disabled, and they are what stops the theory passing merely because every
  host returns the same answer.

MaintenanceOptInClientTests.DefaultIsOff needed no change: it connects to the
in-process server on loopback, which no provider recognizes, so Disabled is
still the right answer there.

The three API entries go back into Unshipped; eng/public-api.py --check agrees
the file is sorted. Also preserved each source file's existing BOM state -
RedisEnterpriseOptionsProvider.cs has none, and writing one in would have put
an unrelated first-line change in the diff.
@mgravell mgravell changed the title Reinstate auto-enlistment for the hosted providers Smart client handoffs: reinstate auto-enlistment for the hosted providers Sep 18, 2026
The hosted providers now enlist by default, so callers on Redis Cloud, Redis
Enterprise and Azure Managed Redis get this behaviour without touching a gated
member; keeping the knobs behind [Experimental] no longer made sense.

Removed the attribute everywhere, the [SER010] prefix from the PublicAPI
entries, and the suppression from NoWarn and the maintenance toys. SER010 is
added to the reserved-IDs list in Experiments.cs so it is never recycled.

docs/exp/SER010.md stays, since the help URL points at it, rewritten in the
retired form. The Redis Enterprise cluster-flag prerequisite that only lived
there moves into ServerMaintenanceEvent.md, which also loses its experimental
note and its stale "Disabled everywhere, until auto-enlistment lands" wording.
@mgravell mgravell changed the title Smart client handoffs: reinstate auto-enlistment for the hosted providers Smart client handoffs: reinstate auto-enlistment for the hosted providers and remove [Experimental] Oct 6, 2026
Comment thread docs/ServerMaintenanceEvent.md Outdated
Co-authored-by: Philo <philon@microsoft.com>
@mgravell
mgravell merged commit 0fd86a1 into main Oct 7, 2026
7 of 9 checks passed
@mgravell
mgravell deleted the marc/maint-auto-by-default branch October 7, 2026 14:33
mgravell added a commit that referenced this pull request Oct 8, 2026
…ER010 (#3248), RetryPolicy doc fix (#3265)

Conflicts, resolved in v4 terms:

- Directory.Build.props: main retired SER010 from NoWarn; v4 had retired SER013 and still
  suppresses SER012 and SER014. NoWarn is v4's list without SER010, and the comment names all
  eight retired IDs (SER011 stays deliberately unsuppressed).
- src/RESPite/Shared/Experiments.cs: both retirements recorded - main's SER010 line
  (MaintenanceNotifications) and v4's SER013 note, whose renumbering explanation now says SER010
  has since retired too. The MaintenanceNotifications constant goes, as on main; nothing in v4's own
  code used it.
- src/StackExchange.Redis/PublicAPI/PublicAPI.Unshipped.txt: union of both sides (main's three
  provider overrides return to Unshipped), then eng/public-api.py sorted it - and RESPite's
  Unshipped file, which was already out of order on v4.

Everything else merged cleanly. Build clean with analyzers on every TFM; RESPite tests pass; main
suite net10.0 and net8.0 pass apart from known flakes, plus one unreproduced
QueuedResultTests failure noted in the ledger's watch list.
FloatingMilkshake pushed a commit to FloatingMilkshake/MechanicalMilkshake that referenced this pull request Oct 9, 2026
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [StackExchange.Redis](https://seredis.dev/) ([source](https://github.com/StackExchange/StackExchange.Redis)) | nuget | minor | `3.3.1` → `3.4.0` |

---

### Release Notes

<details>
<summary>StackExchange/StackExchange.Redis (StackExchange.Redis)</summary>

### [`v3.4.0`](https://github.com/StackExchange/StackExchange.Redis/releases/tag/3.4.0)

[Compare Source](StackExchange/StackExchange.Redis@3.3.1...3.4.0)

#### Major Changes

- In RESP3, by default the library now uses **separate** connections for pub/sub; see [#&#8203;3264](StackExchange/StackExchange.Redis#3264)
- Smart-client-handoffs is now enabled by default on Redis Enterprise and AMR; see [#&#8203;3248](StackExchange/StackExchange.Redis#3248)
- Some logging events incorrectly used duplicated ids - resolved; see [#&#8203;3266](StackExchange/StackExchange.Redis#3266)

#### All Changes

- Recover promptly from a cluster failover; fix the configuration channel under RESP3 and with a channel prefix ([#&#8203;3254](StackExchange/StackExchange.Redis#3254)) by [@&#8203;mgravell](https://github.com/mgravell) in [#&#8203;3255](StackExchange/StackExchange.Redis#3255)
- Preserve binary members as VectorSetRange bounds by [@&#8203;Laurianti](https://github.com/Laurianti) in [#&#8203;3256](StackExchange/StackExchange.Redis#3256)
- Smart client handoffs: reinstate auto-enlistment for the hosted providers and remove \[Experimental] by [@&#8203;mgravell](https://github.com/mgravell) in [#&#8203;3248](StackExchange/StackExchange.Redis#3248)
- docs: fix retry-category test in the custom RetryPolicy sample by [@&#8203;mgravell](https://github.com/mgravell) in [#&#8203;3265](StackExchange/StackExchange.Redis#3265)
- Fix duplicate LoggerMessage event ids; add a guard test by [@&#8203;mgravell](https://github.com/mgravell) in [#&#8203;3266](StackExchange/StackExchange.Redis#3266)
- Scan: observe the faults of abandoned page tasks after a sync timeout or dispose ([#&#8203;3260](StackExchange/StackExchange.Redis#3260)) by [@&#8203;mgravell](https://github.com/mgravell) in [#&#8203;3267](StackExchange/StackExchange.Redis#3267)
- Report the outcome of every physical connection attempt, identifying the client certificate used by [@&#8203;mgravell](https://github.com/mgravell) in [#&#8203;3257](StackExchange/StackExchange.Redis#3257)
- Use a dedicated pub/sub connection under RESP3 by default by [@&#8203;mgravell](https://github.com/mgravell) in [#&#8203;3264](StackExchange/StackExchange.Redis#3264)

#### Tooling

- docs(agents): add create-implementation-plan-for-redis-api-change skill by [@&#8203;uglide](https://github.com/uglide) in [#&#8203;3268](StackExchange/StackExchange.Redis#3268)

#### New Contributors

- [@&#8203;Laurianti](https://github.com/Laurianti) made their first contribution in [#&#8203;3256](StackExchange/StackExchange.Redis#3256)

**Full Changelog**: <StackExchange/StackExchange.Redis@3.3.1...3.4.0>

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate CLI](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC4xNDkuMCIsInVwZGF0ZWRJblZlciI6IjQ0LjE0OS4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Reviewed-on: https://git.milkshake.services/FloatingMilkshake/MechanicalMilkshake/pulls/9
Reviewed-by: Milkshake <floatingmilkshake@noreply.milkshake.services>
FloatingMilkshake pushed a commit to FloatingMilkshake/Azusa that referenced this pull request Oct 9, 2026
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [StackExchange.Redis](https://seredis.dev/) ([source](https://github.com/StackExchange/StackExchange.Redis)) | nuget | minor | `3.3.1` → `3.4.0` |

---

### Release Notes

<details>
<summary>StackExchange/StackExchange.Redis (StackExchange.Redis)</summary>

### [`v3.4.0`](https://github.com/StackExchange/StackExchange.Redis/releases/tag/3.4.0)

[Compare Source](StackExchange/StackExchange.Redis@3.3.1...3.4.0)

#### Major Changes

- In RESP3, by default the library now uses **separate** connections for pub/sub; see [#&#8203;3264](StackExchange/StackExchange.Redis#3264)
- Smart-client-handoffs is now enabled by default on Redis Enterprise and AMR; see [#&#8203;3248](StackExchange/StackExchange.Redis#3248)
- Some logging events incorrectly used duplicated ids - resolved; see [#&#8203;3266](StackExchange/StackExchange.Redis#3266)

#### All Changes

- Recover promptly from a cluster failover; fix the configuration channel under RESP3 and with a channel prefix ([#&#8203;3254](StackExchange/StackExchange.Redis#3254)) by [@&#8203;mgravell](https://github.com/mgravell) in [#&#8203;3255](StackExchange/StackExchange.Redis#3255)
- Preserve binary members as VectorSetRange bounds by [@&#8203;Laurianti](https://github.com/Laurianti) in [#&#8203;3256](StackExchange/StackExchange.Redis#3256)
- Smart client handoffs: reinstate auto-enlistment for the hosted providers and remove \[Experimental] by [@&#8203;mgravell](https://github.com/mgravell) in [#&#8203;3248](StackExchange/StackExchange.Redis#3248)
- docs: fix retry-category test in the custom RetryPolicy sample by [@&#8203;mgravell](https://github.com/mgravell) in [#&#8203;3265](StackExchange/StackExchange.Redis#3265)
- Fix duplicate LoggerMessage event ids; add a guard test by [@&#8203;mgravell](https://github.com/mgravell) in [#&#8203;3266](StackExchange/StackExchange.Redis#3266)
- Scan: observe the faults of abandoned page tasks after a sync timeout or dispose ([#&#8203;3260](StackExchange/StackExchange.Redis#3260)) by [@&#8203;mgravell](https://github.com/mgravell) in [#&#8203;3267](StackExchange/StackExchange.Redis#3267)
- Report the outcome of every physical connection attempt, identifying the client certificate used by [@&#8203;mgravell](https://github.com/mgravell) in [#&#8203;3257](StackExchange/StackExchange.Redis#3257)
- Use a dedicated pub/sub connection under RESP3 by default by [@&#8203;mgravell](https://github.com/mgravell) in [#&#8203;3264](StackExchange/StackExchange.Redis#3264)

#### Tooling

- docs(agents): add create-implementation-plan-for-redis-api-change skill by [@&#8203;uglide](https://github.com/uglide) in [#&#8203;3268](StackExchange/StackExchange.Redis#3268)

#### New Contributors

- [@&#8203;Laurianti](https://github.com/Laurianti) made their first contribution in [#&#8203;3256](StackExchange/StackExchange.Redis#3256)

**Full Changelog**: <StackExchange/StackExchange.Redis@3.3.1...3.4.0>

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate CLI](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC4xNDkuMCIsInVwZGF0ZWRJblZlciI6IjQ0LjE0OS4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Reviewed-on: https://git.milkshake.services/FloatingMilkshake/Azusa/pulls/9
Reviewed-by: Milkshake <floatingmilkshake@noreply.milkshake.services>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants