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: redis/go-redis
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v9.20.0
Choose a base ref
...
head repository: redis/go-redis
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v9.21.0
Choose a head ref
  • 13 commits
  • 56 files changed
  • 10 contributors

Commits on May 29, 2026

  1. fix(pubsub): prevent double reconnect in releaseConn (#3833)

    When a connection is both not usable and has a bad conn error,
    releaseConn would call reconnect twice — first establishing a new
    connection, then immediately closing it to create another. Add early
    return after the first reconnect to avoid the wasted connection.
    cxljs authored May 29, 2026
    Configuration menu
    Copy the full SHA
    65d6abd View commit details
    Browse the repository at this point in the history

Commits on Jun 4, 2026

  1. fix(command): ignore unknown fields in CLUSTER SHARDS response (#3843)

    The CLUSTER SHARDS 'nodes' field contains a list of all nodes within
    the shard. Each individual node is a map of attributes that describe
    the node. Some attributes are optional and more attributes may be added
    in the future.
    
    Previously, the parser returned an error on any unrecognized field,
    causing breakage whenever the server introduces new attributes. Now
    unknown fields at both the shard level and node level are silently
    discarded, matching the forward-compatible behavior already used
    elsewhere in this codebase.
    
    Signed-off-by: Madelyn Olson <[email protected]>
    madolson authored Jun 4, 2026
    Configuration menu
    Copy the full SHA
    974e717 View commit details
    Browse the repository at this point in the history

Commits on Jun 8, 2026

  1. chore(deps): bump codecov/codecov-action from 6 to 7 (#3845)

    Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 6 to 7.
    - [Release notes](https://github.com/codecov/codecov-action/releases)
    - [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md)
    - [Commits](codecov/codecov-action@v6...v7)
    
    ---
    updated-dependencies:
    - dependency-name: codecov/codecov-action
      dependency-version: '7'
      dependency-type: direct:production
      update-type: version-update:semver-major
    ...
    
    Signed-off-by: dependabot[bot] <[email protected]>
    Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
    dependabot[bot] authored Jun 8, 2026
    Configuration menu
    Copy the full SHA
    a4b234f View commit details
    Browse the repository at this point in the history

Commits on Jun 11, 2026

  1. fix(ft.hybrid): Always generate vector param names if they are not pr…

    …ovided by the user (#3844)
    
    * feat(search): always pass FT.HYBRID vector data via PARAMS
    
    Redis no longer supports inline vector blobs in FT.HYBRID. Always pass
    vector data through the PARAMS mechanism, generating a unique parameter
    name when VectorParamName is not provided. Update the related comments
    and drop the now-obsolete 8.6+ skip guards in the integration tests.
    
    * chore(search): add tests for generating vector param names
    
    * doc the limitations
    
    * use local params
    
    * Potential fix for pull request finding
    
    Co-authored-by: Copilot Autofix powered by AI <[email protected]>
    
    * Potential fix for pull request finding
    
    Co-authored-by: Copilot Autofix powered by AI <[email protected]>
    
    * fix build
    
    ---------
    
    Co-authored-by: Copilot Autofix powered by AI <[email protected]>
    ndyakov and Copilot authored Jun 11, 2026
    Configuration menu
    Copy the full SHA
    e1a2d68 View commit details
    Browse the repository at this point in the history
  2. fix(push): fix peeking when push name is truncated (#3842)

    * fix(push): Fix peeking when push name is truncated
    
    * fix(push): fix peeking malformated push
    
    * protect agains potential panic
    ndyakov authored Jun 11, 2026
    Configuration menu
    Copy the full SHA
    10dc44f View commit details
    Browse the repository at this point in the history
  3. chore(release): 9.20.1 (#3847)

    * bump version to 9.20.1
    
    * Add release notes
    ndyakov authored Jun 11, 2026
    Configuration menu
    Copy the full SHA
    a13416b View commit details
    Browse the repository at this point in the history
  4. feat(command): add zero-copy GetToBuffer and SetFromBuffer (#3834)

    * feat(command): add zero-copy GetToBuffer and SetFromBuffer
    
    Adds two new methods on the StringCmdable interface that let callers read
    and write Redis string values directly into/from pre-allocated byte
    buffers, eliminating the per-call payload allocation that the regular
    Get/Set methods incur.
    
    Public API:
      GetToBuffer(ctx, key, buf) *ZeroCopyStringCmd
      SetFromBuffer(ctx, key, buf) *StatusCmd
      ZeroCopyStringCmd { Val() int; Bytes() []byte; Result() (int, error) }
    
    Available on *Client, *ClusterClient, *Ring, *Conn and Pipeliner via
    
    * fix(proto): avoid stale line[0] after bufio refill in ReadStringInto
    
    ReadLine() returns a slice into bufio.Reader's internal buffer (the
    ReadSlice fast path). The follow-up io.ReadFull / Discard calls on
    r.rd can trigger bufio.fill(), which copies new source bytes over the
    slot line[0] used to point at. The post-read
    
        if line[0] == RespVerbatim { ... }
    
    check on line 520 of reader.go could therefore observe a refilled byte
    rather than the original response type. When that refilled byte
    happened to be '=' (0x3D, RespVerbatim) — for example the head of a
    bulk-string payload whose tail forced the refill — a regular `$<n>...`
    Fix: capture line[0] into a local `isVerbatim` boolean before any
    further read on r.rd, and use that local for both the pre-read length
    check and the post-read prefix-stripping branch.
    
    Regression test (internal/proto/reader_test.go): a payload sized to
    exactly DefaultBufferSize, all '=' bytes. After ReadLine consumes the
    header, bufio holds (bufsize - headerLen) payload bytes; the final
    headerLen bytes io.ReadFull asks for force bufio.fill(), which places
    payload-tail + trailing \r\n at position 0 of bufio.buf — i.e.
    '=' at the slot line[0] points at. Against the buggy code the test
    fails with n=32764 wanted 32768; with the fix all 10 ReadStringInto
    tests pass.
    
    * chore(ci): bump bench test timeout to 11m
    
    * chore(bench): cap CI bench wrappers at 64 KiB; cover 10 MiB via test
    
    * fix(bench): keep proto Reader benches O(1) in b.N via loopFrameReader
    
    * fix(proto): drain payload on buffer-too-small; drop verbatim handling
    
    Three related cleanups to ReadStringInto and the cmd it backs:
    
    1. Buffer-too-small now drains the unread payload + trailing CRLF before
       returning the error, so the underlying connection stays aligned for
       the next reply. Without this drain, a too-small caller buffer left
       the bulk payload sitting in the bufio stream — the connection would
       then be returned to the pool in a corrupted state, and the next user
       of that connection would parse the leftover bytes as a malformed
       response. The existing test only checked the first call's error and
       missed this; it now feeds two consecutive bulk-string replies and
       asserts the second parses cleanly. Verified failure-mode by reverting
       just the drain: the follow-up read fails with
       `can't parse reply="hello" reading string into buffer`.
    
    2. Drop RespVerbatim handling from ReadStringInto. The only public
       caller is GetToBuffer, which issues GET, and GET never returns a
       verbatim string — RESP3 verbatim replies come from server-info
       commands like CLIENT INFO / DEBUG OBJECT / LOLWUT. Keeping the
       branch re-introduces a hazard class where the response-type byte
       read from a stale line[0] after a bufio refill can be misinterpreted
       as the verbatim format tag (the bug fixed in
       "fix(proto): avoid stale line[0] after bufio refill in ReadStringInto").
       Removing the branch eliminates the class structurally and drops an
       O(n) memmove on the verbatim path (`copy(buf, buf[4:n])`).
    
       The unit test that previously asserted verbatim parsing is reshaped
       to assert verbatim now surfaces as an error, and the
       BulkStringStartingWithEqRefill regression test is kept (and its
       comment updated) as a guard against re-introducing verbatim support
       without re-introducing the bug.
    
    3. Add a godoc paragraph to ZeroCopyStringCmd.Clone explaining the
       deep-copy semantics (clone gets a freshly allocated buffer, not a
       slice of the caller's memory) and noting that Clone is unreachable
       through normal client flows: it is only called from cluster fan-out
       routing in osscluster_router.go for multi-shard commands like
       DBSIZE / KEYS / FLUSHDB, and ZeroCopyStringCmd is only produced by
       GetToBuffer which issues GET — a single-key command never fanned
       out. Combined with NoRetry()=true blocking the retry path, Clone
       exists only to satisfy the Cmder interface and to keep the cmd usable
       if a future caller does fan out.
    
    * fix(zerocopy): clone fails explicitly; reset cmd.n; update doc
    
    Three small follow-ups to ZeroCopyStringCmd identified during a full
    review of the zero-copy buffer code path:
    
    1. Clone() now returns a marked clone whose readReply drains the
       network reply and surfaces an explicit error rather than producing
       silently-wrong results. Cloning a ZeroCopyStringCmd has no
       well-defined semantics: the cmd writes into caller-owned memory
       (the buf passed to GetToBuffer), and a clone can neither share that
       buf (sibling clones would race, last-writer wins) nor allocate its
       own buf (the result would be invisible to whoever asked for the
       original cmd's reply). The previous implementation chose the second
       path and lost the result; this commit chooses to fail loudly.
    
       The Cmder interface requires Clone, so we still return a clone.
       The clone is flagged via a new private field, and readReply checks
       that flag, calls rd.DiscardNext() to keep the connection aligned,
       and returns a clear error ("redis: ZeroCopyStringCmd cannot be
       cloned (cmd writes into caller-owned memory)").
    
       Pre-setting cmd.err in Clone would NOT work because pipelineReadCmds
       overwrites it with whatever readReply returns — the drain-and-error
       path is what actually survives the framework.
    
       In practice this path is unreachable through normal client flows:
       Clone is only called from cluster fan-out routing in
       osscluster_router.go for multi-shard commands (DBSIZE / KEYS /
       FLUSHDB), and ZeroCopyStringCmd is only produced by GetToBuffer
       which issues GET — a single-key command routed to one shard, never
       fanned out. Combined with NoRetry()=true blocking the retry path,
       the clone path is exercised only by explicit caller code.
    
       A new Ginkgo spec asserts both that processing a clone returns the
       expected error and that a follow-up GetToBuffer on the same client
       succeeds (the drain keeps the connection usable).
    
    2. readReply now resets cmd.n = 0 on entry so that an error after a
       previous successful run can't leak the old data through Bytes().
       Cheap defensive belt-and-braces — the caller is supposed to check
       Err() first, but this removes a stale-state failure mode entirely.
    
    3. The ZeroCopyStringCmd type godoc previously claimed "no data is
       consumed beyond the header line" on the buffer-too-small path.
       That stopped being true after the previous commit added the drain
       to ReadStringInto. Updated to match reality.
    
    Verified end-to-end against Redis 8.8 in Docker:
      orig GetToBuffer: OK
      cloned cmd processing: errored as expected
      follow-up GetToBuffer: OK — connection stays aligned
    
    * docs(string): clarify that SetFromBuffer is API symmetry, not a perf path
    
    SetFromBuffer and Set(ctx, key, buf, 0) dispatch to the same `case []byte`
    branch in proto.Writer and produce byte-identical output on the wire. The
    "zero-copy on send" property comes from bufio.Writer.Write bypassing its
    internal buffer for payloads larger than that buffer — a property of
    bufio, not of SetFromBuffer. Set([]byte) gets the same behaviour for
    free.
    
    Without this clarification, callers reading the godoc could reasonably
    infer that SetFromBuffer is a performance optimisation over Set, choose
    it for that reason, and be disappointed when the bench numbers come back
    indistinguishable from Set([]byte). The added paragraph spells out the
    actual rationale (API symmetry with GetToBuffer for code that uses the
    buffer-based pattern on both sides) and tells callers who don't care
    about that symmetry that Set(ctx, key, buf, 0) is equally efficient.
    
    No behaviour change.
    ndyakov authored Jun 11, 2026
    Configuration menu
    Copy the full SHA
    74d9bb0 View commit details
    Browse the repository at this point in the history
  5. feat(streams): support explicit LIMIT 0 in XTRIM/XADD trimming via XT…

    …rimLimitDisabled sentinel (#3848)
    
    Redis treats XTRIM/XADD trimming LIMIT 0 (valid only with the ~ approximate
    flag) as "disable the trimming effort cap entirely", which is different from
    omitting LIMIT (implicit default of 100 * stream-node-max-entries examined
    entries). The command builders only emitted LIMIT when limit > 0, so callers
    could never send an explicit LIMIT 0.
    
    Following the KeepTTL = -1 precedent, add the XTrimLimitDisabled = -1
    sentinel and a shared appendXTrimLimit helper used by XAdd, xTrim and
    xTrimMode:
    
      - limit == 0 keeps the historical behavior: no LIMIT clause is sent;
      - limit > 0 emits LIMIT <limit> as before;
      - limit < 0 (XTrimLimitDisabled) emits an explicit LIMIT 0.
    
    No public signatures or struct fields change, and existing callers passing 0
    produce byte-identical commands. Exact ("=") trim commands (XTrimMaxLen,
    XTrimMinID and their Mode variants) still never emit LIMIT; XAddArgs keeps
    the existing upstream behavior of passing a user-supplied Limit through even
    without Approx (Redis rejects it server-side), now explicitly documented on
    the field.
    
    Add server-less unit tests asserting the constructed command args for the
    omitted/positive/disabled-limit and exact-trim cases across all XTRIM
    variants and XAdd.
    TheRealMal authored Jun 11, 2026
    Configuration menu
    Copy the full SHA
    641294c View commit details
    Browse the repository at this point in the history

Commits on Jun 15, 2026

  1. chore(deps): bump rojopolis/spellcheck-github-actions (#3852)

    Bumps [rojopolis/spellcheck-github-actions](https://github.com/rojopolis/spellcheck-github-actions) from 0.60.0 to 0.61.0.
    - [Release notes](https://github.com/rojopolis/spellcheck-github-actions/releases)
    - [Changelog](https://github.com/rojopolis/spellcheck-github-actions/blob/master/CHANGELOG.md)
    - [Commits](rojopolis/spellcheck-github-actions@0.60.0...0.61.0)
    
    ---
    updated-dependencies:
    - dependency-name: rojopolis/spellcheck-github-actions
      dependency-version: 0.61.0
      dependency-type: direct:production
      update-type: version-update:semver-minor
    ...
    
    Signed-off-by: dependabot[bot] <[email protected]>
    Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
    dependabot[bot] authored Jun 15, 2026
    Configuration menu
    Copy the full SHA
    bf57a51 View commit details
    Browse the repository at this point in the history

Commits on Jun 17, 2026

  1. feat(tx): skip redundant UNWATCH in Tx.Close when no WATCH is active (#…

    …3854)
    
    Tx.Close issued UNWATCH unconditionally before returning the connection to
    the pool, adding a round trip to every Watch transaction even when nothing
    was watched or when EXEC had already discarded the watched keys.
    
    Track whether a WATCH is still active on the transaction (watchArmed) and
    only issue UNWATCH from Close when it is. The flag is set on a successful
    WATCH and cleared only when EXEC actually ran: a nil error (committed),
    TxFailedErr (a watched key changed, EXEC returned nil), or an EXECABORT
    error (a queued command was rejected, EXEC discarded the transaction) — all
    of which release the watched keys server-side. Any other error may be
    reported before EXEC executes (for example -LOADING on the MULTI reply) or
    on a broken connection, so the flag is left set and Close still sends
    UNWATCH rather than risk leaving a watch on a pooled connection.
    
    This removes the extra UNWATCH on the common WATCH/.../EXEC path and on the
    no-key Watch path, and never returns a connection to the pool with an
    active WATCH. No exported API changes; the only wire difference is the
    elided no-op UNWATCH.
    
    Tests assert, on the wire (recording hook) and server-side (INFO
    commandstats), that UNWATCH is elided after a committed EXEC, an aborted
    EXEC, an EXECABORT, and a no-key Watch, and is still sent on every path
    where no EXEC cleared the watch: an error before EXEC, a read-only decision,
    a non-transactional pipeline, an empty TxPipelined, multi-key bail, a WATCH
    re-armed after EXEC, a panic in fn, a failed WATCH, and a server error
    reported before EXEC runs. A manual Unwatch case asserts no second UNWATCH
    from Close, and a PoolSize=1 reuse case asserts no watch leaks onto a
    pooled connection.
    
    Co-authored-by: Claude Opus 4.8 <[email protected]>
    fcostaoliveira and claude authored Jun 17, 2026
    Configuration menu
    Copy the full SHA
    5484b0b View commit details
    Browse the repository at this point in the history
  2. feat(pubsub): introduce timeouts for Ping on channel.initHealthCheck (#…

    …3819)
    
    * replace context.TODO with context.WithTimeout in channel.initHealthCheck before calling c.pubSub.Ping
    
    * fix(pubsub): create fresh timeout context for each health check
    
    * create context with timeout after lock acquisition
    
    * export pingTimeout and reconnectTimeout
    
    ---------
    
    Co-authored-by: Nedyalko Dyakov <[email protected]>
    abdellani and ndyakov authored Jun 17, 2026
    Configuration menu
    Copy the full SHA
    1f0ea0e View commit details
    Browse the repository at this point in the history

Commits on Jun 18, 2026

  1. fix(maintnotifications): keep ModeAuto fail-open (#3853)

    * fix maintnotifications ModeAuto fallback
    
    * retire maintnotifications connections on downgrade
    
    * guard maintnotifications tracking during downgrade
    terrorobe authored Jun 18, 2026
    Configuration menu
    Copy the full SHA
    1cfa927 View commit details
    Browse the repository at this point in the history

Commits on Jun 22, 2026

  1. chore(release): 9.21.0 (#3857)

    * bump version to 9.21.0
    * add release notes
    ndyakov authored Jun 22, 2026
    Configuration menu
    Copy the full SHA
    1551837 View commit details
    Browse the repository at this point in the history
Loading