Visitar URL original
Comparing 3.4.5...3.4.20 · seredis/StackExchange.Redis · GitHub
Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: seredis/StackExchange.Redis
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 3.4.5
Choose a base ref
...
head repository: seredis/StackExchange.Redis
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 3.4.20
Choose a head ref
  • 15 commits
  • 106 files changed
  • 3 contributors

Commits on Oct 16, 2026

  1. Do not abandon a connection attempt before ConnectTimeout has elapsed (…

    …#2894) (#3277)
    
    The heartbeat's Connecting branch aborted an in-flight attempt as soon as the
    retry policy said a retry was due. With the default ExponentialRetry at half
    the connect timeout that is ~2.5s, so any attempt whose name resolution or TLS
    handshake took longer than that was killed inside its own ConnectTimeout budget
    and reported as a connect timeout. A primary DNS server that does not answer
    costs ~5s per lookup on glibc, so on Linux every reconnect attempt died during
    resolution and the connection never left Connecting.
    
    Require ConnectTimeout to have elapsed before the retry policy may abort an
    attempt. The socket connect already carries its own ConnectTimeout cancellation;
    this check bounds TLS and the handshake after it.
    mgravell authored Oct 16, 2026
    Configuration menu
    Copy the full SHA
    5625693 View commit details
    Browse the repository at this point in the history
  2. Read HELLO "server" before trusting "version" as the Redis API version (

    #3279)
    
    * Read HELLO "server" before trusting "version" as the Redis API version (#3278)
    
    Valkey reports its own version as HELLO "version" ("server" => "valkey",
    "version" => "9.1.2") while INFO still says redis_version:7.2.4; Garnet
    keeps "version" as the Redis API version ("server" => "redis") and adds
    "garnet_version". Both the interactive and subscription connections issue
    HELLO, so the detected version depended on whether the last reply processed
    was HELLO or INFO, and version-gated commands (DELEX for LockRelease) were
    sent to servers that don't have them.
    
    Now "version" only sets the Redis API version when "server" is redis; for a
    recognised non-Redis server it feeds the product variant instead, and for an
    unrecognised server it is ignored. A "<vendor>_version" HELLO field sets the
    product variant in the Garnet shape. The in-process test server can emulate
    both measured shapes, and a HELLO-only test (INFO disabled) pins the rule
    down on RESP2 and RESP3.
    
    * Snapshot the HELLO "version" reader instead of allocating a string
    
    Parse it as a Version (no string) once we know "server" is redis; only the
    vendor path, which stores the string anyway, materializes it.
    
    * Parse the HELLO "version" straight from UTF-8
    
    Adds Format.TryParseVersion(ReadOnlySpan<byte>): net10.0 has a built-in
    UTF-8 Version.TryParse; elsewhere a Utf8Parser loop over the dotted
    components, so no char transcoding and, downlevel, no string. The HELLO
    handler uses it via the function-pointer TryParseScalar overload; a parity
    test keeps it in step with the char/string overloads.
    
    * Use IsScalar on the snapshot reader instead of a separate flag
    
    * Match the HELLO "server" name case-insensitively
    
    * Pin the raw Version parts (unspecified = -1) for the UTF-8 parser, and the gate normalisation
    
    System.Version treats "7.4" as (7, 4, -1, -1) and "7.4.0" as (7, 4, 0, -1); the
    UTF-8 overload must produce the same raw parts as the char/string overloads,
    and IsAtLeast/IsEqual must then treat the unspecified parts as zero.
    
    * UTF-8 TryParseVersion: defer to the char overload for exact parity
    
    The goal is identical results to the pre-existing char path for identical
    text, whatever System.Version does with it (-1 for unspecified parts,
    whitespace and sign tolerance per part), not a tidier parser. net10.0
    fast-paths the built-in UTF-8 Version.TryParse; anything it rejects, and
    every other TFM, transcodes and calls the char overload. On .NET Framework
    that still allocates a string, which is acceptable. The parity test now
    covers the whitespace/sign cases.
    mgravell authored Oct 16, 2026
    Configuration menu
    Copy the full SHA
    1f13e2d View commit details
    Browse the repository at this point in the history
  3. Sentinel soft-fail follow-ups: discovery failure as RedisConnectionEx…

    …ception, late sentinel init, clear LastException (#3280)
    
    Follow-ups to #3187 (AbortOnConnectFail=false for sentinel), prompted by the
    investigation of #3271:
    
    - GetSentinelMasterConnection with a config that has no endpoints of its own
      and nothing discovered threw AggregateException(InvalidOperationException
      "No nodes to consider") from the seed connect. It now throws the discovery
      RedisConnectionException, as it did before #3187.
    - A sentinel connection whose initial connect soft-failed never ran
      InitializeSentinel, because only the connect paths called it; such a
      connection never subscribed to +switch-master and tracked failovers only
      through the fallback polling. InitializeSentinel is now idempotent and is
      also reached from a non-first reconfigure that detects a sentinel.
    - The discovery failure recorded as LastException at connect time stayed
      there after recovery and was folded into every later "no connection
      available" message. It is cleared once the managed connection is restored.
    - Document what abortConnect means for sentinel connections.
    mgravell authored Oct 16, 2026
    Configuration menu
    Copy the full SHA
    84765ee View commit details
    Browse the repository at this point in the history

Commits on Oct 21, 2026

  1. Sentinel: re-resolve the primary when it is demoted on a healthy conn…

    …ection (#3281)
    
    * fix(sentinel): re-resolve the primary when it is demoted on a healthy connection
    
    A sentinel-managed connection notices an in-place demotion of its primary
    (INFO replication reports role:slave) but only re-elects among the nodes it
    already knows. When sentinel promoted a node at an address the client has
    never seen and the +switch-master announcement was missed, there is no
    primary among them and nothing asks sentinel again: writes fail with
    "No connection (requires writable - not eligible for replica)" until restart.
    
    The sentinel connection's heartbeat now checks, from local state only,
    whether each managed connection's sentinel-reported primary is connected
    and reporting itself as a replica (or was never added). If so, a recovery
    attempt runs off the heartbeat thread, at most one per managed connection:
    - it asks sentinel first; no answer: retry on a later heartbeat;
    - sentinel names a node held as a connected replica (sentinel has not
      failed over yet, or our view of that node's role is stale): re-read that
      node's role with one INFO replication, rather than reconfiguring every
      node once per heartbeat for as long as sentinel lags (where INFO cannot
      be sent, fall through to the switch rather than ask sentinel forever);
    - otherwise switch, as +switch-master would have.
    An attempt is skipped while the managed connection is already reconfiguring,
    or once either multiplexer is disposed; disconnected primaries are left to
    the existing reconnect timer. The heartbeat scan also forgets disposed
    children, as the +switch-master handler does.
    
    Tests: an in-process sentinel (InProcessSentinelServer) and a standalone
    SetReplicaOf on the toy server. The demotion-to-unknown-address test fails
    without the fix (no primary at all) and passes with it; a second test holds
    sentinel on the demoted node and checks that sentinel is still polled but
    the connection is not reconfigured on every heartbeat, then follows sentinel
    once it catches up.
    
    Refs #1891, #2487, #1670
    
    * Review follow-ups: make SwitchPrimary claim the reconfigure gate before reshaping the connection
    
    - SwitchPrimary now claims the reconfigure gate (activeConfigCause) BEFORE rewriting the
      endpoint list or retiring servers, via new TryClaimReconfigure/ReleaseReconfigureClaim and
      a 'claimed' handover parameter on ReconfigureAsync. Previously a reconfigure that started
      between RecoverDemotedPrimary's pre-checks and ReconfigureAsync's own CAS would refuse the
      switch's reconfigure AFTER the connection had been reshaped, leaving retired servers and no
      primary for at least a heartbeat; concurrent switches could also interleave their
      Clear/TryAdd endpoint rewrites. A lost race now touches nothing and the heartbeat retries.
    - RecoverDemotedPrimary hands sentinel's already-validated answer to SwitchPrimary
      (reportedPrimary), so the switch acts on the answer the guards evaluated instead of
      re-querying and possibly acting on a different one while sentinel flaps - also removing
      redundant blocking sentinel round-trips from the claimed section.
    - A successful demotion recovery clears connection.LastException (as the restore handler
      does) once the reported primary is connected as a primary, so a stale soft-connect
      discovery failure stops polluting timeout messages after the client is healthy again.
    - currentSentinelPrimaryEndPoint is now volatile: it is written by SwitchPrimary on
      worker/handler threads and read by the heartbeat demotion check.
    - The disposed-child cleanup shared by the +switch-master handler and the heartbeat sweep
      is extracted into ForgetDisposedChild.
    - DemotedPrimaryDoesNotReconfigureWhileSentinelStillNamesIt no longer uses wall-clock quiet
      detection plus a fixed observation window with an exact event count: it waits for a
      stretch of three sentinel polls with zero configuration changes, re-baselining on
      stragglers - deterministic on loaded machines, and faster.
    - docs/Configuration.md documents how a sentinel-managed connection tracks failovers,
      including demotion-in-place re-resolution.
    
    ---------
    
    Co-authored-by: mgravell <marc.gravell@gmail.com>
    rebaseandpanic and mgravell authored Oct 21, 2026
    Configuration menu
    Copy the full SHA
    a3f389a View commit details
    Browse the repository at this point in the history

Commits on Oct 22, 2026

  1. Route as a cluster only with a slot map; take HELLO mode/role from th…

    …e interactive connection only (#3285)
    
    * Route as a cluster only with a slot map; take HELLO mode/role from the interactive connection (#3284)
    
    A proxied deployment can answer HELLO with "mode: cluster" while INFO says
    redis_mode:standalone and every CLUSTER subcommand is refused (Azure Managed
    Redis with the Enterprise clustering policy). Under RESP3 pub/sub now has its
    own connection (#3264), whose handshake is HELLO alone: when that leg alone
    reconnected, its HELLO retyped the server as a cluster with no INFO to follow,
    the resulting reconfigure put the multiplexer into cluster mode with no slot
    map, and cross-slot commands were refused client-side.
    
    - Cluster *routing* now requires a slot map: servers that identify as a cluster
      but give us no map are routed as a single standalone server (new event id
      133). The map is never cleared once populated, so an established cluster
      whose refresh is refused (#3275) keeps routing as one. When a map does
      arrive, stale redundant-primary flags from an earlier election are cleared.
    - AnyServer accepts a cluster-typed server while routing as standalone
      (ServerType.IsRoutableAs), so such a server stays a valid target.
    - HELLO "mode" and "role" are applied only from the interactive connection;
      the subscription connection does no discovery and must not overwrite
      server-level facts.
    
    Tests: HelloClusterModeUnitTests reproduces the subscription-only reconnect
    under both protocols (fails under RESP3 before this change);
    ClusterCommandsRefusedUnitTests asserts standalone routing and a working
    cross-slot MGET when CLUSTER is refused. The in-process server gains an
    overridable HelloModeValue so HELLO and INFO can disagree.
    
    * Derive the slot-map check from allocation; reject inverted or out-of-range slot ranges
    
    The routing decision in ReconfigureAsync needs only "is there any slot map",
    not a count. Add ServerSelectionStrategy.IsSlotMapEmpty, derived from whether
    the map has been allocated: the map is allocated only immediately before a
    write, writers never store null, and nothing clears an entry, so a non-null
    map always covers at least one slot. CountCoveredSlots remains for the log
    line in the cluster branch, where it has always been.
    
    That derivation is only sound if nothing can allocate without writing, so
    UpdateClusterRange now ignores an inverted or out-of-range range instead of
    allocating and then writing nothing (or indexing out of bounds). SlotRange
    does not validate ordering and the reply-driven paths build ranges straight
    from what the server sent.
    
    SlotMapUnitTests covers the rejected ranges leaving the map unallocated.
    mgravell authored Oct 22, 2026
    Configuration menu
    Copy the full SHA
    7870ba9 View commit details
    Browse the repository at this point in the history
  2. Fail connection-dropped commands with RedisConnectionException, never…

    … raw or cancelled (#3283) (#3287)
    
    When a connection failed, most queued commands failed with RedisConnectionException
    but a few received the raw transport exception (IOException, or the RESPite
    writer's InvalidOperationException) or a TaskCanceledException with no token in
    play. Three sources, all confirmed with the reporter's repro:
    
    - RecordConnectionFailed builds its detailed RedisConnectionException only for
      the bridge's current connection and only on the first report; a repeat report
      (the write path and the read loop both reporting), or one against a connection
      the bridge has already replaced, drained the queue with the raw inner
      exception - or with null, which the result box turns into a cancellation
      (every EOF path in the read loop reports with no exception).
    - The write-lock catch fails the message properly and then reports the
      connection, which drained the same still-queued message again with the raw
      exception; last write wins.
    - FailDueToNoConnection, TimedOutBeforeWrite and the fail-fast bin completed the
      message via Cancel(); ExecuteAsyncImpl then races to fault the task with the
      real exception, and the box's thread-pool continuation sometimes wins. A batch
      has no caller translating the result at all.
    
    Fix: the drain always uses a RedisConnectionException (wrapping whatever it was
    given, with the failure type), and the bridge's refusals set the exception the
    caller path would have built instead of cancelling. Message.Cancel itself is
    unchanged; transactions rely on it for aborted conditions.
    
    ConnectionFailureExceptionTests covers the drain (null and IOException inner,
    on a bridge-less connection, i.e. the non-current path) and the no-connection
    refusal observed at the message's own task. Reporter's repro, 90 s: 884
    TaskCanceledException + 415 InvalidOperationException before; none after.
    
    Fixes #3283
    mgravell authored Oct 22, 2026
    Configuration menu
    Copy the full SHA
    10caa39 View commit details
    Browse the repository at this point in the history
  3. Tests: tolerate clock skew on absolute hash-field expiries; wait for …

    …quiescence before asserting backlog counters (#3288)
    
    Two of the recurring Windows CI flakes. HashFieldTests' DateTime-based expiries are computed on the client clock and
    read back on the server's (in WSL), and the TTL came back as 7200001ms against a hard 7200000 ceiling: five tests in one
    run. BacklogTests.TotalOutstandingIncludesBacklogQueue asserted TotalOutstanding == 0 the instant the last backlogged
    ping completed, while the reconnect's handshake traffic was still in flight (seen as 7 and 1; six failures in the last
    twelve days).
    mgravell authored Oct 22, 2026
    Configuration menu
    Copy the full SHA
    53aab24 View commit details
    Browse the repository at this point in the history
  4. Connection groups: a member answering LOADING triggers an immediate r…

    …e-probe and re-select, like a breaker trip (#3289)
    
    The group's fast-path only reacted to ConnectionFailureType.CircuitBreaker. A server that is loading fails every
    reconnect during the handshake, so commands routed to it die in the backlog without a reply, and the breaker only
    observes replies - it can never be what moves traffic away. This is the shape of the recurring Windows CI failure of
    WithRetry_FailsOverBetweenGroupsOnLoading (six attempts in twelve days, always 20 x 5000ms backlog timeouts): when the
    heartbeat rather than the test's command met the first LOADING, nothing ever failed over.
    
    LOADING now joins CircuitBreaker in the fast-path; the health check still decides where traffic goes, so a member whose
    probe passes is kept (WithRetry_WhenFailoverNeverArrives_KeepsRetryingSameMember). New LoadingRerouteTests fails
    without the change.
    mgravell authored Oct 22, 2026
    Configuration menu
    Copy the full SHA
    cb09290 View commit details
    Browse the repository at this point in the history
  5. IBatch: complete commands refused by a poisoned connection; divert a …

    …batch's tail on reconnect (#3286)
    
    * Complete a message refused by a poisoned connection; divert a batch's tail on reconnect (#3282)
    
    Since #3205 a connection whose write faulted is poisoned and refuses further
    writes, so that no reply can be matched to the wrong caller. That refusal
    returned WriteFailure without completing the message. Single commands were
    unaffected, as ExecuteAsyncImpl/ExecuteSyncImpl translate a non-success result
    into an exception for the caller; but RedisBatch.Execute writes a list via
    PhysicalBridge.TryEnqueue, which does not act on the result, so once one
    message of a batch faulted the connection, the rest of that batch was neither
    queued nor completed and its tasks were awaited forever (3.2.0 to 3.4.5).
    
    - WriteMessageToServerInsideWriteLock: the IsWriteFaulted refusal now fails and
      completes the message, as every other refusal to write already does
      (FailDueToNoConnection, TimedOutBeforeWrite, QueueOrFailMessage, the
      write-lock catch, the transaction sub-command loop).
    - TryEnqueue(list): once NeedsReconnect is set part-way through the batch, the
      remaining messages take the same path a single command would at the top of
      TryWriteSync - backlog or fail, per policy - instead of being written to a
      connection known to be dead.
    
    BatchWriteFailureTests injects a write failure mid-batch under both backlog
    policies and asserts that every task completes and that any successful result
    is real. The reporter's repro (400 batches in flight, CLIENT KILL in a loop)
    hung within 13 s on main and ran 6.6 M batches in 120 s with this change.
    
    Fixes #3282
    
    * Batch: route the tail to the backlog when the held connection is poisoned, not just while the reconnect flag is set
    
    The reconnect flag is cleared as soon as TryConnect starts a replacement connection, on another thread; a batch
    still holding the previous PhysicalConnection could then write its remaining messages to it and have them refused
    with WriteFailure. Seen on the Windows CI runner (2 of 3 attempts); reproduces locally 12/25 pinned to two cores.
    Also make the test's fail-fast variant wait for the reconnect before its verification read, which is the other
    way the two-core run failed.
    mgravell authored Oct 22, 2026
    Configuration menu
    Copy the full SHA
    74c52bf View commit details
    Browse the repository at this point in the history
  6. Tests: don't race the connect's own reconfigures in AllClusterCommand…

    …sRefusedIsQuiet (#3290)
    
    A freshly connected multiplexer fires fire-and-forget reconfigures of its own
    (connection-restored per physical connection, autoconfigure field changes), and
    ReconfigureAsync returns false while one is in flight. The test asserted on
    that return value directly, so it lost the race on the Windows runner
    (main @ 74c52bf, net10.0/RESP3).
    
    Hoist the retry loop HelloClusterModeUnitTests already used for this into
    Poll.ReconfigureAsync and use it in both tests; dump the captured log after
    the assertions so a future failure shows what was actually logged.
    mgravell authored Oct 22, 2026
    Configuration menu
    Copy the full SHA
    a07aabb View commit details
    Browse the repository at this point in the history

Commits on Oct 23, 2026

  1. Send primary-only commands to the primary when the caller prefers a r…

    …eplica (#3291)
    
    * Send primary-only commands to the primary when the caller prefers a replica
    
    KeyDelete, KeyExpire (milliseconds), LockExtend and LockRelease pick a server up front to check its features. With PreferReplica that picked a replica, and the primary-only message was then rejected with 'Command cannot be issued to a replica'. GetFeatures now upgrades the flags the same way Message does.
    
    Fixes #2821
    
    * Mirror Message.SetPrimaryOnly exactly; explain the PEXPIRE expiry in the test
    
    ---------
    
    Co-authored-by: Marc Gravell <marc.gravell@gmail.com>
    AsadIqbal-Dev and mgravell authored Oct 23, 2026
    Configuration menu
    Copy the full SHA
    6cbaa37 View commit details
    Browse the repository at this point in the history
  2. Give multi-message sub-commands and injected prefix commands a write …

    …time (#3292) (#3293)
    
    Sub-commands of an IMultiMessage (WATCH, condition reads, MULTI, the queued
    operations; also SCRIPT LOAD and PTTL) are written inside the lock without
    passing through SetEnqueued, as are the SELECT/READONLY/ASKING/PREPARE
    prefixes injected ahead of a command. With a zero write time the heartbeat
    sees them as overdue by the process uptime; since #2965 such no-box messages
    count as sync timeouts, and together with an idle connection that satisfies
    the dead-socket heuristic while the reply is still in flight.
    
    Sub-commands now inherit the parent's write time (sharing its budget,
    including backlog time); prefix commands are stamped when written, via a
    single helper replacing four identical blocks.
    mgravell authored Oct 23, 2026
    Configuration menu
    Copy the full SHA
    946f13a View commit details
    Browse the repository at this point in the history
  3. Refresh package pins: 10.0.13 servicing family and tooling (#3294)

    Shipped BCL dependencies move 10.0.5 -> 10.0.13 (System.IO.Hashing 10.0.12 -> 10.0.13);
    test/tooling packages to current. Removes the unreferenced PerformanceCounter pin and
    documents that the StackExchange.Redis 2.13.17 pin is the baseline projects' previous-major
    comparison, not a lagging version.
    
    Analyzer.Testing 1.1.4 brings NuGet.Packaging 7.0.3 -> System.Security.Cryptography.Pkcs 9.0.6,
    which NuGet audit rejects; a direct Pkcs 10.0.13 reference in Build.Tests lifts it.
    
    Held back for separate PRs: xunit.v3 4.x (needs Microsoft.Testing.Platform 2.5+, which drops the
    VSTest bridge under the .NET 10 SDK, plus discoverer/attribute changes) and StyleCop 1.3-beta
    (92 new rule hits in src).
    mgravell authored Oct 23, 2026
    Configuration menu
    Copy the full SHA
    9d30a98 View commit details
    Browse the repository at this point in the history
  4. xunit.v3 4.0 on Microsoft.Testing.Platform (#3295)

    * xunit.v3 4.0 on Microsoft.Testing.Platform
    
    xunit.v3 4.x depends on xunit.v3.mtp-v2, which needs Microsoft.Testing.Platform 2.5+, and
    that drops the VSTest bridge under the .NET 10 SDK. So: global.json selects the MTP runner
    for dotnet test, the four test projects set UseMicrosoftTestingPlatformRunner and stop
    referencing the VSTest adapters (Microsoft.NET.Test.Sdk, xunit.runner.visualstudio,
    xunit.v3.runner.console), and CI.yml/build.ps1 use the MTP dotnet test shape (--project /
    --solution, --report-xunit-trx; the GitHub Actions reporter self-registers as an extension).
    
    xunit 4 fallout: the per-protocol TheoryDiscoverer overrides the new 6-parameter
    CreateTestCasesForDataRow; the fault-injector tests use [Parallelization(Mode = None)];
    xUnit1069 is suppressed for the round-trip tests, whose Timeout is a hang guard; net481
    regains System.Reflection.Metadata and Microsoft.CSharp, previously transitive via Test.Sdk.
    
    * CI: the Ubuntu composite action also moves to the MTP dotnet test shape; enable the GitHub reporter
    
    --report-github is off by default under Microsoft.Testing.Platform (the VSTest logger was on
    by name), so both jobs ask for it explicitly, with the summary limited to failures as before.
    
    * Follow-ups for the MTP runner: analyzer tests join the solution, build.ps1 wording and framework, docs
    
    Build.Tests was never in StackExchange.Redis.slnx, so dotnet test --solution skipped it.
    The -NetCoreOnlyTests path moves to net10.0: two test projects have no net8.0, and the
    runner fails a project lacking the requested framework instead of skipping it. AGENTS.md,
    the skills and the fault-injector README use the --project / --filter-class forms.
    
    * Use the platform TRX reporter; observe the test-side tasks the new runner reports as unobserved
    
    xunit 4.0.2's --report-xunit-trx wrote a truncated file for the 6.8k-test run (ends mid-element;
    dorny/test-reporter: 'Invalid XML'), locally as well as in CI. Microsoft.Testing.Extensions.TrxReport's
    --report-trx produces a complete report (validated: parses, 6828 UnitTestResult elements).
    
    Microsoft.Testing.Platform also prints every UnobservedTaskException at the end of the run, which
    VSTest never showed. All of the ones seen are in test code, not the library: the in-process server's
    background work logging to xunit after its test ended (xunit v3 throws 'no currently active test');
    BatchWriteFailureTests never reading its Task.WhenAll's exception; TcpForwarder's losing copy
    direction. Each now observes or swallows its own failure.
    
    * VectorSetInfo: vset-uid is a zero-based counter, so do not assert it is non-zero
    
    Measured on a fresh test server: the first vector set created after startup reports vset-uid 0,
    the next 1, and so on. CI starts a fresh server every run, and the xunit 4 discovery order makes
    VectorSetInfo(Binary) under RESP2 the first test to create one, so the old assertion failed there
    and nowhere else. Check the parsed value against the raw VINFO reply instead.
    mgravell authored Oct 23, 2026
    Configuration menu
    Copy the full SHA
    9a1fabe View commit details
    Browse the repository at this point in the history
  5. StyleCop.Analyzers 1.2.0-beta.556 -> 1.3.151-beta, and the tidy-up it…

    … asks for (#3296)
    
    New rule hits, all fixed rather than suppressed: SA1407 (parenthesise comparisons combined with the
    non-short-circuit & and |), SA1143 (multi-line conditionals use the expanded layout), SA1504 (an
    accessor pair is all single-line or all multi-line), SA1315 (override/implementation parameter names
    match the member they implement), SA1653 (<content> on secondary partial declarations), plus single
    SA1117/SA1413/SA1500 sites. 46 sites in RESPite, 175 in StackExchange.Redis, 3 in RESPite.Benchmark.
    
    SA1315 found a real bug: KeyPrefixedDatabase.SortedSetRangeAndStore (the sync, key-prefixed overload)
    declared its first two parameters as (destination, source) against the interface's (source,
    destination) and forwarded them by name, so it read from the destination and wrote into the source.
    Regression test: WithKeyPrefixTests.SortedSetRangeAndStoreUsesTheRightKeys.
    
    Public API: AzureOptionsProvider/AzureManagedRedisOptionsProvider.AfterConnectAsync rename 'muxer'
    to the base's 'multiplexer' (recorded in PublicAPI.Unshipped); every other rename is on an internal
    type or an explicit interface implementation.
    mgravell authored Oct 23, 2026
    Configuration menu
    Copy the full SHA
    aa1e5c0 View commit details
    Browse the repository at this point in the history
Loading