This is a minor release adding new features and bug fixes. There are no breaking changes; upgrading from 9.20.x is a drop-in replacement.
🚀 Highlights
Zero-copy GetToBuffer / SetFromBuffer
Two new StringCmdable methods let callers read and write Redis string values directly into and from pre-allocated byte buffers, eliminating the per-call payload allocation that Get/Set incur:
GetToBuffer(ctx, key, buf) *ZeroCopyStringCmd // reads into buf; ZeroCopyStringCmd { Val() int; Bytes() []byte; Result() (int, error) }
SetFromBuffer(ctx, key, buf) *StatusCmdGetToBuffer decodes the bulk reply straight into the caller-owned buf (no intermediate allocation); a buffer that is too small returns an error after draining the payload, so the connection stays aligned for the next reply. SetFromBuffer is provided for API symmetry — it dispatches to the same []byte writer path as Set(ctx, key, buf, 0) and produces byte-identical output on the wire. Available on *Client, *ClusterClient, *Ring, *Conn and Pipeliner.
Explicit LIMIT 0 for stream trimming
Redis treats XTRIM/XADD approximate-trim (~) LIMIT 0 as "disable the trimming effort cap entirely", which differs from omitting LIMIT (the implicit 100 * stream-node-max-entries default). The command builders previously only emitted LIMIT when limit > 0, so callers could never send an explicit LIMIT 0. Following the KeepTTL = -1 precedent, the new XTrimLimitDisabled = -1 sentinel now emits an explicit LIMIT 0; limit == 0 keeps the historical no-LIMIT behavior, so existing callers produce byte-identical commands.
(#3848) by @TheRealMal
✨ New Features
- Zero-copy buffer string commands: new
GetToBuffer/SetFromBufferonStringCmdableand theZeroCopyStringCmdresult type, reading/writing string values into caller-owned buffers without per-call payload allocation (#3834) by @ndyakov XTrimLimitDisabledsentinel:XTRIM/XADDapproximate trimming can now send an explicitLIMIT 0to disable the trim effort cap, via the newXTrimLimitDisabled = -1sentinel (#3848) by @TheRealMal- PubSub health-check timeouts:
channel.initHealthChecknow bounds thePingit issues with a fresh per-check timeout context (the exportedpingTimeout/reconnectTimeout) instead ofcontext.TODO(), so a stuck health-check Ping can no longer block indefinitely (#3819) by @abdellani - Skip redundant
UNWATCHinTx.Close: a transaction now tracks whether aWATCHis still active (watchArmed) and only issuesUNWATCHonClosewhen it is, removing an extra round trip on the commonWATCH/.../EXECand no-keyWatchpaths while never returning a connection to the pool with an active watch (#3854) by @fcostaoliveira
🐛 Bug Fixes
maintnotificationsModeAutofail-open:ModeAutonow stays fail-open when the server does not support maintenance notifications — connections are retired and tracking is guarded during downgrade so the client keeps working instead of erroring (#3853) by @terrorobe
👥 Contributors
We'd like to thank all the contributors who worked on this release!
@abdellani, @fcostaoliveira, @ndyakov, @terrorobe, @TheRealMal