Skip to content

Merge main into v4 (3.4.29): lazy subscription connection, IPv6 parse, batch profiling, API promotion - #3322

Open
mgravell wants to merge 9 commits into
v4from
marc/main-sync-3.4.29
Open

mgravell wants to merge 9 commits into
v4from
marc/main-sync-3.4.29

Conversation

@mgravell

Copy link
Copy Markdown
Collaborator

Brings v4 up to main@02b9ce4e (the 3.4.29 release). Four of main's eight commits merged clean (#3311 coverage ledger + audit skill, #3312 RedisType.Bitmap + keyspace tokens, #3314 ConnectionGroupMember name fix, #3317 LICENSE split); the rest were ported by intent onto the new core. Merge with a merge commit, not a squash — the merge carries the ancestry record, so no separate "Record main@…" commit is needed unless this gets squashed.

Ported by intent

  • Restore: make the dedicated subscription connection lazy #3310 lazy subscription connection. v4 was already half-lazy: DialSubscriptionSocketForConfigurationChannel no-ops without a configuration channel. The port completes it: ActivateServer gates on RequiresEagerSubscriptionConnection (configuration channel, or existing subscriptions that may need to move to this endpoint), the dial method becomes DialSubscriptionSocketIfRequired with the broadened guard (AnySubscribed(), matching the RESP3-downgrade path's own condition), and the endpoint-state log says NotRequired for a socket nobody has asked for, via a new HasDedicatedSubscriptionConnection probe — asking never dials. Main's IsSubscriberConnectedOrNotRequired is re-expressed over the connection manager rather than bridge fields.
  • Parse unbracketed IPv6 ip:port from the server (#1422) #3307 unbracketed IPv6. Format.TryParseServerEndPoint + call sites merged clean (ClientInfo, ClusterConfiguration, sentinel, MaintenanceNotificationReader — rename detection carried the old PhysicalConnection.Maintenance hunk across). The old ResultProcessor MOVED/ASK site is now Contexts/RespRedirect.cs, patched to match. Two v4-only sites main never had were fixed the same way: RespHandshake's ROLE-reply parsing glued host + ":" + port, which turns an unbracketed IPv6 host into an address with no port — it now uses the (host, port) overload, as main's sentinel fix did.
  • Profile commands sent in a batch (#2175) #3313 batch profiling (Batched commands missing in ProfiledCommandEnumerable #2175). The ported ProfilingTests.Batch failed on v4 too — the new core has the same bug by a different route: a batch bypasses Dispatch, where single sends start their profiling records. SendRun now starts the same record per operation on the batch path (same database rule: the command's database, -1 for database-free commands), and a batched operation now carries its Command identity. A transaction's run stays unprofiled, as it always was.
  • public-api.py: apply *REMOVED* at promotion; promote the API surface since 3.4.0 #3316 public-api promotion. The 3.4.29 surface moved into Shipped via the merge; the 38 Unshipped entries that the promotion made duplicates (LagAware*, FailbackHealthCheck, AfterConnectAsync rename, …) are dropped, and the v4-only surface stays unshipped. The eng/public-api.py REMOVED-at-promotion change merged clean.

Not ported (nothing to port)

Follow-ups

  • docs/CommandCoverage.md was generated against main's surface; re-running the audit-command-coverage skill on v4 (where e.g. HOTKEYS/XCFGSET exist) will shrink the gap list.
  • Verified: full build (all TFMs, analyzers green) and the full test suite against the docker topology — 7972 passed / 0 failed / 167 skipped (one flaky integration failure on the first run did not reproduce).

mgravell and others added 9 commits October 30, 2026 10:15
The dedicated pub/sub connection (always under RESP2; under RESP3 unless
SharedSubscriptionConnection is enabled) was opened eagerly as part of
activating every endpoint, so a client that never subscribes to anything
still paid for a second socket per node, and a second reconnect per node
on every failover. Seen on AMR, whose options provider disables the
configuration channel: 3.4.x showed two RESP3 connections where 3.0.x
showed one.

ActivateServer now opens that connection only when there is something to
subscribe to straight away: a configuration channel, or subscriptions that
already exist (and may need to move to this endpoint). Otherwise it is
created on first use, via the existing GetBridge paths; with the default
backlog policy the first SUBSCRIBE queues until it is up.

The connect wait ("second leg") and the maintenance handoff deadline
treated an absent subscription bridge as a disconnected one, which would
have left connects hanging; they now consider a connection that has not
been asked for as not required. The endpoint-state connect log says
"NotRequired" for that case instead of "Disconnected".

Tests: AzureManagedRedisConnectionCount now asserts one connection before
pub/sub and the expected count after; a new test covers the eager case
when a configuration channel is set; two tests that relied on the eager
subscription connection now subscribe first.
* Command coverage ledger and audit skill

Adds docs/CommandCoverage.md: every upstream Redis command the library does
not expose as a typed API, grouped and with a recorded decision (policy,
not applicable, deferred, todo, deprecated, enum-only, implemented). Seeded
from a diff of redis/redis src/commands (477 specs, @6a5fee67) against the
RedisCommand enum and source; it carries forward the dispositions from
#2055 (blocking is policy, Functions and ACL are deferred) and replaces
that hand-maintained list.

Adds the audit-command-coverage skill and its script, which re-runs the
diff on request and reports only what changed: new upstream commands with
no decision, ledger rows the code now covers, rows the spec no longer has,
and enum members the spec does not know. The script exits non-zero when
the ledger needs editing.

Closes #2055.

* Ledger: CLIENT TRACKING is v4, cluster/COMMAND introspection is administration, CLUSTER SHARDS note corrected

* Ledger: CLIENT PAUSE/UNPAUSE are operator tooling, not applicable
…ent server values (#3312)

* Object-type metadata for native bitmaps (redis/redis#15331), plus the tokens that diff turned up

RedisType gains Bitmap ("bitmap", the TYPE reply for a native roaring
bitmap, 8.12) and Gcra ("gcra", already in the server's type-name table).
KeyNotificationType gains SetBit (emitted under class $ for string bitmaps
and class b for native ones) and the other event names the server emits
that the enum lacked: arinsert, armset, arring, arseek, gcra, pfadd,
sexpire, spersist, sinterstore, zrangestore, and keymiss (class m, side
effect group).

Fixes the Copy token: the server has only ever emitted "copy_to", so the
"copy" token never parsed and a RedisChannel.KeyEvent(KeyNotificationType.Copy)
subscription never fired. The member and its value are unchanged.

Both parsers are generated from [AsciiHash]; tests extend the token tables.

* Drop Gcra: the type was withdrawn before release, so there is nothing for the token to match

* Add sunionstore/sdiffstore events (from the keyspace-notifications docs); drop the history comment on Copy
* Forward the name argument in ConnectionGroupMember's string constructor

The string overload chained to the ConfigurationOptions constructor without passing `name`, so every
member built from a configuration string (the idiom used throughout docs/Failover.md) was named after
its first endpoint by Init() instead of the name the caller gave it. Regression test added.

* Parenthesize a Debug-only assertion that trips SA1407

`endSegmentLength == 0 & endSegmentCommitted == 0` in DebugAssertValid sits inside `#if DEBUG`, so CI
(Release) never compiles it and never saw the StyleCop error; any local Debug build of RESPite failed
with TreatWarningsAsErrors. Introduced by ee47e53 after the StyleCop bump in aa1e5c0.
* Parse unbracketed IPv6 ip:port from the server (#1422)

CLUSTER NODES, MOVED/ASK and CLIENT LIST write IPv6 addresses without
brackets, i.e. "2a06:4e80:3:a::a:7000". The RFC 3986 parser used for
configuration strings reads that as a (valid!) eight-group address with
no port, so cluster nodes ended up as "[2a06:4e80:3:a::a:7000]:0" and
redirections and client addresses were equally wrong.

Add Format.TryParseServerEndPoint for the server's "ip:port" form: when
there is no ']' and more than one ':', split at the last colon and
require both halves to parse; anything else goes to the existing parser
unchanged, so configuration parsing is not touched. Use it at the three
sites that receive server-emitted text.

The sentinel +switch-master handler gets host and port as separate
tokens and was gluing them back together with ':' before parsing; pass
them to the (host, port) overload instead.

* Parse unbracketed IPv6 in MOVING/SMIGRATED endpoints

ParseMigrationEndPoint used the configuration-string parser, so an unbracketed IPv6 address parsed as an address with port 0, which the placeholder check then discarded. Use TryParseServerEndPoint, as the other server-emitted sites do; tests for the MOVING successor and SMIGRATED source/target.
Co-authored-by: mohsinaliwork686-rgb <229973101+mohsinaliwork686-rgb@users.noreply.github.com>
…since 3.4.0 (#3316)

* public-api.py: apply *REMOVED* entries at promotion instead of copying them

The analyzer records a vanished shipped signature as a *REMOVED* line in
the unshipped file. Promotion moved every unshipped line across verbatim,
so a promotion after any such change would have left the shipped file
with both the dead signature and a *REMOVED* copy of it, and the analyzer
would then fail the build on the dead one (RS0017).

Promotion now consumes the marker: the named signature is dropped from the
shipped file, the marker is not carried over, and a marker that names
nothing shipped is an error. A *REMOVED* line already sitting in a shipped
file is also reported. The "+N promoted" note gains "-N removed", and an
unshipped file holding only removals is still emptied.

* Promote the shipped API surface: everything since 3.4.0

eng/public-api.py --promote. Fifty-two StackExchange.Redis entries (the
lag-aware availability surface, FailbackHealthCheck, HealthCheckProbeScope,
the 8.12-independent RedisType and KeyNotificationType tokens) and the
net6.0 SetUserPemCertificate overload move to shipped; the three parameter
renames from #3296 (muxer -> multiplexer on the Azure providers'
AfterConnectAsync, other -> obj on RESPite AsciiHash.Equals) are applied
rather than copied. Every unshipped file is now empty.
* Split third-party notices out of LICENSE so GitHub detects MIT

The LICENSE file carried ~1.1KB of third-party notes after the MIT text,
which drops it below licensee's similarity threshold; GitHub reports the
repo license as "Other" (SPDX NOASSERTION). Keep LICENSE as the bare MIT
text and move the notes, verbatim, to THIRD-PARTY-NOTICES.md.

* Correct the third-party notices

- Redis: BSD-3 only through 7.2; RSALv2/SSPLv1 from 7.4, AGPLv3 added in
  8.0; point at redis.io/legal/licenses (the old /topics/license URL now
  redirects to the docs landing page).
- redis-doc: repo is archived under redis/redis-doc; note that redis/docs,
  its successor, is CC BY-NC-SA for newer content.
- Drop the Redis-64 and BookSleeve notes: neither is part of the
  development solution any more.
- Add the code actually incorporated from other projects (dotnet/runtime,
  dotnet/corefx, aspnet/BasicMiddleware) and the test-only redis-sharp copy,
  none of which the old notes mentioned.
…d IPv6, batch profiling, Bitmap/keyspace tokens, coverage ledger

Ports onto the new core, by intent where the old core's files are gone:

- #3310 lazy subscription connection: ActivateServer now dials the
  subscription socket only when RequiresEagerSubscriptionConnection says
  there is something for it to do (the configuration channel, or existing
  subscriptions); DialSubscriptionSocketForConfigurationChannel becomes
  DialSubscriptionSocketIfRequired with the broadened guard. The endpoint
  log distinguishes a socket nobody asked for (NotRequired) from a down
  one, via HasDedicatedSubscriptionConnection.
- #3307 unbracketed IPv6: Format.TryParseServerEndPoint merged clean; the
  old ResultProcessor MOVED/ASK site is now Contexts/RespRedirect, patched
  to match, and the ROLE reply parse in RespHandshake stops gluing
  host:port (the sentinel mistake, found at two more sites v4 grew).
- #3313 batch profiling (#2175): the ported ProfilingTests.Batch FAILED on
  this core too - a batch bypasses Dispatch, where single sends start
  their records - so SendRun now starts the same record per operation on
  the batch path, and a batched operation carries its Command identity.
  A transaction's run stays unprofiled, as it always was.
- #3314 ConnectionGroupMember name fix merged clean (its CycleBuffer half
  had already arrived via ce5a8cd).
- #3316 public-api promotion: the 3.4.29 surface moved to Shipped; the 38
  Unshipped entries that promotion made duplicates are dropped, and the
  v4-only surface stays unshipped.
- #3311/#3312/#3317 merged clean (coverage ledger + audit skill,
  RedisType.Bitmap + keyspace tokens, LICENSE split).

This branch has not been deployed

No deployments
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