v0.20.1
Documentation only. The README, the package documentation and the docs have the current versions (chromedp v0.20.0, cdproto v0.157.8, remote v0.2.0), the CI matrix and the release history. The README describes termcast and the 43 programs of chromedp/examples.
remote v0.2.0
The remote module requires chromedp v0.20.0 and cdproto v0.157.8, and uses go 1.25. It builds with Go 1.25 and later.
v0.20.0
chromedp supports Go 1.25 and later. Before this release, the module needed Go 1.27 (see #1536).
- The JSON types come from github.com/chromedp/cdproto/cdp/jsonv2. With Go 1.27 or GOEXPERIMENT=jsonv2 they are aliases of the types of the standard encoding/json/v2 and jsontext, so the public API is the same. With Go 1.25 and 1.26 they come from github.com/go-json-experiment/json.
- The modules chromedp, remote and test use go 1.25 and cdproto v0.157.8.
- The CI runs Go 1.25, 1.26 and stable on Linux, stable on Windows and macOS, and the experiment and the compatibility tag.
Wails v3.0.0-beta.28
- Keep macOS app output outside file providers in PR by @taliesin-ai
- Complete setup wizard and show system details in PR by @taliesin-ai
- Improve WebView2 recovery error handling and resource cleanup on Windows in PR by @leaanthony
- Improve WebView2 recovery on Windows to prevent app crashes and controller strandings in PR by @taliesin-ai
- Correct macOS media authorization guidance in PR by @taliesin-ai
- Fix right-clicking a Linux system tray icon running the click handler as well as opening the menu, so an attached window toggled on every right-click (#6018)
- Keep WebView2 callback handlers alive until native release on Windows in PR by @taliesin-ai
🤖 This is an automated nightly release generated from the latest changes on master.
Installation:
go install github.com/wailsapp/wails/v3/cmd/wails3@v3.0.0-beta.28
9.23.0
This is a minor release. It contains everything from 9.23.0-beta.1, so these notes cover the full 9.22.0 → 9.23.0 upgrade.
- Go 1.26 is now the minimum Go version (#4060). The
godirective of the root module and of the submodules moved from1.24to1.26, as part of the dependency bumps forgovulncheckfindings. Projects that build with Go 1.24 or 1.25 must upgrade the toolchain. - Each
Clienthas a dedicated pipeline connection pool (#4002). This includes failover clients and cluster node clients.Pipeline,TxPipeline, and autopipeline operations use this pool, so they do not compete with regular commands for main-pool connections. The pool is for burst capacity only: it dials only when necessary, an unused pool holds zero connections, and if the pool is full an operation uses the main pool. SetPipelinePoolSize: -1to get the previous single-pool behavior. AutoPipelineOptions.MaxBatchByteshas a default of 128 KiB (before, there was no limit). The default prevents a write/reply deadlock; it is not a throughput control. The buffers of the pipeline pool also have a default of 128 KiB.
AutoPipelineOptions.FullDuplexFastSubmitis removed (#4018). The submit channel is replaced by a queue that takes a full wave of commands in one lock, so the separate fast path is not necessary. Remove the field from your options.Options.ClientSideCacheRefreshRecencyWindowis removed (#4034). Refresh-on-invalidate now reads again every cached entry of an invalidated key and does not look at how recently the entry was read. On a write-heavy keyspace this means refresh traffic across the full resident cache. Remove the field from your options.LocalCache.LRUClock()is removed (#4034). It returned the recency token that the removed recency window used, and has no replacement. Remove calls to it.ClientSideCacheInvalidationBatchWindowserved stale values in beta.1 (#4033). Under load the batcher applied only ~15% of invalidations, and the cache served invalidated values with a ~100% hit rate. 9.23.0 applies all of them. If you used this option on beta.1, upgrade.
Set AutoPipelineOptions.FullDuplex to enable the full-duplex mode of the automatic pipeliner. The default mode sends one batch per round trip. In full-duplex mode the engine holds one pipeline-pool connection, and a writer goroutine and a reader goroutine move the ordered command stream in both directions at the same time. On a high-latency link each command completes in approximately one round-trip time (RTT) on a single connection. On a 50 ms WAN profile: ~389k ops/s at 52 ms p50, against ~207k ops/s at 116 ms on the half-duplex ordered path.
The mode works on both faces of a standalone Client (AutoPipeline() and AsyncAutoPipeline()), and natively on a ClusterClient when routing goes to masters only. On a cluster, one child engine per master sends each command to the node that owns its slot, and follows MOVED / ASK redirects and retryable replies (LOADING, READONLY, ...) through the usual cluster redirect path. With replica routing (ReadOnly, RouteByLatency, or RouteRandomly), the autopipeliner uses the half-duplex shard flushers, which obey the shard picker. Config().FullDuplex reports the mode in effect.
Options:
FullDuplexWindow— the maximum number of commands in flight (backpressure).FullDuplexIdleTimeoutandFullDuplexMaxHold— when the engine returns the held connection to the pool. The pool hooks then do re-authentication and maintenance-notification handoffs.MaxFlushDelay— now also used in full-duplex mode (#4014). The writer waits only when enough commands are in flight, so low-concurrency callers keep the 1×RTT behavior. WithMaxFlushDelay=250µsat 1024 callers: +22% ops/s and −26% p99. The default is 0, so this is opt-in.NumShards— on a standaloneClient, the number of full-duplex engines (#4022). See below.
Pipeline() on the full-duplex connection (#4021). Standalone Client only. Before, ap.Pipeline() used a pooled connection for each Exec. It now submits the batch to the engine as one contiguous run (FDPipelined), so replies come back in submit order, as on a dedicated connection. At 256–1024 callers with 10 commands per Exec: +49% to +52% throughput on one socket instead of ~150. Errors, retries, hooks, and metrics behave like an ordinary pipeline. A batch that cannot use the stream (a blocking command, a per-command read timeout, a diverted command, or a batch larger than the queue) falls back to the ordinary pipeline. TxPipeline stays on a pooled connection, because MULTI/EXEC needs connection affinity. On a ClusterClient, ap.Pipeline() is still the ordinary cluster pipeline. A batch that the queue cannot admit now reserves its slots and waits in a first-come line, so single commands cannot starve a long pipeline (#4057, merged as part of #4021) by @vlady-kotsev.
Several engines on one client (#4022). With FullDuplex, NumShards > 1 now means N engines on one client: one pool, one hook chain, N held connections. Commands route by a hash of their first key, so commands on the same key stay on one connection and keep their order without Unordered. Keyless commands are distributed round-robin. An FDPipelined batch stays on one engine. The pipeline pool must hold at least NumShards connections for each full-duplex autopipeliner. More engines help only under high concurrency: with 10-command pipelines, 8 engines were 30% slower than 1 at 8 callers, equal at about 128 callers, and 2.2× faster at 1024 callers.
The full-duplex path supports blocking commands, Options.Limiter (one admission per written batch), per-command and pipeline hooks, OTel metrics, retry budgets (a NoRetry command is never sent again after it is on the wire), and seamless maintenance handoffs. Limits of the stream model:
- A hook can observe a command, but cannot stop it. A
ProcessHookthat returns without callingnextdoes not cancel the command on the full-duplex path; the command is already queued on the held connection. Run policy or kill-switch hooks on a plain client or on the half-duplex autopipeliner. - Diverted commands are not ordered with the stream. The engine diverts blocking commands, connection-hostile commands, and managed
HIMPORTcommands. On the async face, wait for the result of a diverted command before you submit a command that depends on it. - Single commands on the full-duplex stream do not use the client-side cache. If CSC is enabled, a cacheable command on the stream does not read or write the cache.
- Duration metrics are recorded per reply. A command that fails before it reaches the reader (lease failure, limiter denial, retry exhaustion, close) calls the error callback but records no operation-duration sample.
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
AutoPipelineOptions: &redis.AutoPipelineOptions{
FullDuplex: true, // stream commands on one held connection, ~1 RTT each
},
})
defer rdb.Close()
// Deferred face: calls return immediately; result accessors block until executed.
ap, err := rdb.AsyncAutoPipeline()
if err != nil {
panic(err)
}
cmds := make([]*redis.StatusCmd, 0, 1000)
for i := 0; i < 1000; i++ {
cmds = append(cmds, ap.Set(ctx, fmt.Sprintf("key:%d", i), i, 0))
}
for _, cmd := range cmds {
if err := cmd.Err(); err != nil {
// handle error
}
}
// Or the blocking face — a drop-in Cmdable where each call blocks like a
// plain client while concurrent callers share the full-duplex pipe:
// ap, err := rdb.AutoPipeline()
// val, err := ap.Get(ctx, "key").Result()
Runnable examples: example/autopipeline (all faces), example/autopipeline-fullduplex (blocking face, Pipeline() and FDPipelined), and example/autopipeline-fullduplex-async (async face, Submit, NumShards) (#4061).
Experimental: the auto-pipelining APIs can change in a minor release.
(#4002, #4014, #4018, #4021, #4022) by @ndyakov
Two new functions for the experimental shared-tracking client-side cache. They have no effect unless CSC is enabled (#3989) by @ndyakov:
- Refresh-on-invalidate (
Options.ClientSideCacheRefreshOnInvalidate): when an invalidation push arrives, the client reads every cached entry of that key again in the background, so the next reader does not pay the miss. Each invalidation of a cached key costs one background read.ClientSideCacheInvalidationBatchWindowcollects invalidation-driven deletes into background batches; without it, the connection reader applies each delete inline. - Miss coalescing (
Options.ClientSideCacheCoalesceMisses): concurrent cache misses are pipelined on one tracked connection. Each miss keeps its own per-key command (no rewrite toMGET), so it is safe on a cluster. Each reply is written to the cache with the connection's tracking generation, so the server can invalidate it.
Both options need the built-in cache (ClientSideCacheConfig, or ClientSideCache set to a *LocalCache). With a custom Cache implementation they are ignored.
The cache read path is also faster (#4034, #4035, #4036, #4037) by @ndyakov. A hit no longer writes a timestamp to the entry; a second-chance bit marks reads, and is set only under eviction pressure. The cache key is built in pooled scratch memory, the key list is built only on a miss, and nil/status/bulk-string hits are decoded without allocation. Against 9.23.0-beta.1 on a warm cache: +18% to +57% reads/s from 4 to 1024 callers, p50 from 2.6–5.4 µs to 1.0–1.5 µs, with identical hit rates and 100% of invalidations applied.
RouteByLatency selects the node with the strictly minimum latency. The measurement has noise (the mean of ten pings, refreshed at most every 10 s), so all clients can select the same node from a set of nodes with almost equal latency. In one production system, GET rates across five replicas in one availability zone had a 590× spread.
The new ClusterOptions.RouteByLatencyTolerance widens the selection to every node within the tolerance of the fastest node, and distributes reads across them round-robin with the ShardPicker. A node in a different availability zone stays outside a sensible tolerance, so zone locality is kept. The default is zero (strict minimum). The option is also on FailoverOptions, for clients from NewFailoverClusterClient; the plain NewFailoverClient does not support latency routing. (#3973) by @jozenstar
The same work fixed a routing defect: the client recorded the nearest healthy node only when that node was also the fastest overall. A node that fails fast (for example, a refused connection) hid every healthy node, and reads went to the failing node. The client now records the healthy minimum separately. (#3994) by @jozenstar
Client.Conn() returns its connection to the parent pool on Close. If you ran AUTH or SELECT on it, later commands on the pooled client run as that ACL user and in that DB. The new Client.EphemeralConn() returns the same sticky connection, but Close removes it from the pool instead. The cost is one dial per EphemeralConn. Client.Conn() is unchanged. (#4039) by @saddamr3e
- Full-duplex auto-pipelining:
AutoPipelineOptions.FullDuplex, withFullDuplexWindow/FullDuplexIdleTimeout/FullDuplexMaxHold, on standalone and cluster clients (#4002) by @ndyakov Pipeline()on the full-duplex connection:ap.Pipeline()andFDPipelinedsubmit a batch to the engine as one contiguous run, on a standaloneClient(#4021) by @ndyakov- Several full-duplex engines on one client:
NumShards > 1withFullDuplexon a standaloneClient, with key-hash routing (#4022) by @ndyakov AutoPipelineOptions.MaxQueuedCommands: an opt-in hard limit on accepted, not-yet-completed autopipeline commands. At the limit a command fails immediately withErrAutoPipelineQueueFull, so memory does not grow without limit when the server is slow. WithFullDuplex, the ordered stream is bounded byFullDuplexWindowinstead (a full window blocks the submitter, it does not reject), andMaxQueuedCommandslimits only the commands that run outside the stream (blocking, connection-hostile,Do). The default0means no limit (#4070) by @ndyakov- Dedicated pipeline pool by default:
PipelinePoolSizehas a default ofDefaultPipelinePoolSize(10) on each client. If the pool is full, an operation uses the main pool. Set-1to disable the pool (#4002, #3959) by @ndyakov - CSC refresh-on-invalidate and miss coalescing:
Options.ClientSideCacheRefreshOnInvalidate(withClientSideCacheInvalidationBatchWindow) andOptions.ClientSideCacheCoalesceMisses(#3989) by @ndyakov Client.EphemeralConn: a sticky connection that is removed from the pool onClose, for session-scopedAUTH/SELECTwork (#4039) by @saddamr3eRouteByLatencyTolerance: distributes reads across nodes with almost equal latency, on cluster clients and onNewFailoverClusterClient(#3973) by @jozenstarAutoPipeliner.WaitClosed: blocks until the drain of accepted commands completes, and returns the drain result. Use it in a wrapper that must not close shared pools during a flush (#3998) by @ndyakovredisotel-nativeWithRecordNilErrors: opt in to countredis.Nilreplies as client errors (see the fix below) (#4025) by @lazergCMSInfo.CellSize: the cell-size field ofCMS.INFOin Redis 8.12 (#4010) by @elena-kolevska
- CSC invalidation batching:
ClientSideCacheInvalidationBatchWindowapplied only ~15% of invalidations under load, so the cache served stale values with a ~100% hit rate. Each batch now takes each shard lock once, and 100% of invalidations are applied (#4033) by @ndyakov - Pool waiter wake-ups:
Putdid not wake a worker that waited for a connection to becomeIDLE, so a streaming-credentials re-auth could stall untilPoolTimeoutand then close the connection (#4028) by @avitenzer. Two related waiter-queue races are fixed: a failed transition left a dead waiter queued, which later moved a connection toUNUSABLEfor nobody (#4072) by @ndyakov - Command key positions: 16 typed commands (
BitOp*,MIGRATE,OBJECT ENCODING/FREQ/IDLETIME/REFCOUNT,XINFO STREAM/GROUPS/CONSUMERS,LMPOP,BLMPOP,SINTERCARD,ZINTERCARD,ZMPOP,BZMPOP) routed by the wrong argument. On aRingthey went to the wrong shard; on a cluster they cost an extraMOVEDround trip. A rawFCALL/FCALL_ROhashed the function name. All now route by their key (#4048) by @ndyakov. RawXGROUP,ZUNION/ZINTER/ZDIFF,SDIFFCARD/SUNIONCARD,TS.NRANGE/TS.NREVRANGE, andHIMPORT SETforms are also fixed (#4022) - Millisecond expirations:
PExpireAt,HPExpireAt, andHPExpireAtWithArgsoverflowed for dates outside the nanosecond range (for example, year 2270), so Redis deleted the key or rejected the command (#4026) by @jakezwang.SetArgs.ExpireAtnow sendsPXATwhen the deadline has millisecond precision; before,EXATdropped the milliseconds (#4053) by @LindseyZ1205 - Cluster
-1timeouts: a-1read/write timeout onClusterOptionsgave node clients the 5 s default instead of no timeout (fixes #4049) (#4050) by @lazerg - OTel
redis.Nilas error: a cache miss (redis.Nil) incrementedredis.client.errorsand was tagged as a server error ondb.client.operation.duration. Nil replies are now classifiedNILand not counted, unlessWithRecordNilErrors(true)is set (fixes #4024) (#4025) by @lazerg - Credential redaction:
SENTINEL SET ... auth-passis now redacted in command rendering (used byredisotel/rediscensusspan attributes), and aMIGRATEpassword with the literal valueauth/auth2is redacted at the correct position (#4011) by @saddamr3e - Min/max aggregator panic: a non-numeric shard reply made
AggMinAggregator/AggMaxAggregatorpanic instead of returning an error fromResult()(#4056) by @Suselz ParseURLskip_verify: an invalid value (for example,skip_verify=yes) was silently treated asfalse.ParseURLandParseFailoverURLnow return an error, like for the other boolean options (#4064) by @orinnzuintptrarguments:uintptrand*uintptrare now encoded like the other unsigned integers; a nil*uintptris encoded as0(fixes #3100) (#4054) by @yamaankhan20- Cluster read routing: the client records the nearest healthy node separately from the overall minimum, so a node that fails fast does not hide the healthy nodes (#3994) by @jozenstar
- Probabilistic
*.INFOforward compatibility: theBF.INFO/CF.INFO/CMS.INFO/TOPK.INFO/TDIGEST.INFOparsers skip unknown fields instead of returning an error. Redis 8.12 addscell sizetoCMS.INFO(#4010) by @elena-kolevska NewClientpanic leak: a panic during construction (for example, a maintnotifications failure inModeEnabled) does not leak the connection pools that already exist, and a typed-nil pool cannot hide the initial panic (#4003) by @ndyakov. The same guards apply toNewFailoverClient(#4002)- File-descriptor leak on rejected connections:
Conn.Closedoes the socket teardown and the unsubscribe/CSC callbacks when the connection is alreadyCLOSED. Before, init and auth failures leaked open descriptors (fixes #3982) (#3985) by @ndyakov - Global logger races: atomics protect the global
LoggerandLogLevel, and call-site attribution is correct again (#3988) by @saddamr3e Conn.onClosedata race: the close hooks that init installs (onCloseandonCscClose) are atomic against a concurrentClose(#3966) by @saddamr3e- Reply-parser hardening: the reply parsers accept zero-length entry arrays (#3995), and
FTHybridCmdreads the full RESP3 map reply without desynchronizing the connection (#3956) by @saddamr3e CLIENT INFOforward compatibility: the parser skips unknown client-flag characters instead of failing the reply (#3977) by @ndyakovGEOSEARCHduplicate args: the command no longer sends duplicate arguments (#3955) by @mehmettokgozMSetEXcluster routing: the constructor sets the first-key position, so typed calls go to the correct slot (#3984) by @shivamrustagi- Maintenance notifications: no endpoint DNS detection when the mode is disabled (#3969) by @Phalanyx
- Autopipeliner
Close: a concurrentClosedoes not block (no re-entrant deadlock), andWaitClosedreturns the drain result (#3998) by @ndyakov - Buffered-push log noise: the buffered-push-data notice in
isHealthyConnis logged only at debug level, so CSC invalidations do not fill the log (#3948) by @ndyakov - Sentinel teardown order: close hooks run in LIFO order, so an autopipeliner drain completes before Sentinel discovery stops, and a closed failover client cannot recreate its Sentinel resources from a late dial (#4002) by @ndyakov
- Pipeline desync containment: if a pre-write push-notification drain fails or a command encoder panics, the
Pipeline/TxPipelinepath removes the connection instead of returning a desynchronized connection to the pool (#4002) by @ndyakov
- CSC read path: second-chance read bit, pooled cache-key scratch, key list built only on a miss, allocation-free decode of simple hits. +18% to +57% reads/s against 9.23.0-beta.1 on a warm cache (#4034, #4035, #4036, #4037) by @ndyakov
- Full-duplex reads and submit queue: replies already in the buffer are read as one group (one read deadline per group, not per reply), and a mutex+slice queue replaces the submit channel, so the writer takes a whole wave in one lock (#4018) by @ndyakov
- Full-duplex
MaxFlushDelay: opt-in write coalescing under load. With 250 µs at 1024–2048 callers: +22% to +25% ops/s and −26% to −29% p99; 30% less CPU at 256 callers (#4014) by @ndyakov - Full-duplex allocations: a ring buffer holds the in-flight queue, and the blocking face uses a pool of batches. 770 B/op → 353 B/op at 2048 concurrent callers (#3970, part of #4002) by @ndyakov
- Zero-copy scan:
Scangets zero-copy semantics, and the RESP reader skips unnecessary data conversions (#3972) by @vlady-kotsev - Autopipeline straggler hold: the engine limits the hold on queued commands when the pipeline pool has a free connection. Uncached p95 on a 50 ms link: 111 ms → 65 ms; real-WAN uncached p99: 314 ms → 177 ms (#3962) by @ndyakov
- Go 1.26 and
govulncheckdependency bumps: the minimum Go version is 1.26, CI defaults to 1.26, and a newgo-versionsjob builds the test matrix fromgo.modplusoldstable/stable(#4060) by @vlady-kotsev - Fast skip gates: tests do a TCP probe of each address before the
Pinggate, which removes ~1.6 min of dial-retry waits when the full stack is not running (#4001) by @ndyakov - Redis Enterprise coverage: the autopipeline suites connect to the RE database and use the suite DB (#3976, #3975), timing assertions scale to the measured RTT (#3978), and the
CLIENT INFOtracking-flag assertion is skipped behind the RE proxy (#3981) by @ndyakov - Docs:
UniversalOptionsdocumentsReadTimeout/WriteTimeout/ContextTimeoutEnabled(#4047) by @lazerg; the README describesDisableIdentityas client identification (CLIENT SETINFO), not identity verification (#4059) by @FanWu-ai - Security policy: send vulnerability reports to the Redis VDP (#3949) by @ndyakov
We thank all the contributors who worked on this release!
@avitenzer, @elena-kolevska, @FanWu-ai, @jakezwang, @jozenstar, @lazerg, @LindseyZ1205, @mehmettokgoz, @ndyakov, @orinnz, @Phalanyx, @saddamr3e, @shivamrustagi, @Suselz, @vlady-kotsev, @yamaankhan20
Full Changelog: https://github.com/redis/go-redis/compare/v9.22.0...v9.23.0
9.23.0
This is a minor release. It contains everything from 9.23.0-beta.1, so these notes cover the full 9.22.0 → 9.23.0 upgrade.
- Go 1.26 is now the minimum Go version (#4060). The
godirective of the root module and of the submodules moved from1.24to1.26, as part of the dependency bumps forgovulncheckfindings. Projects that build with Go 1.24 or 1.25 must upgrade the toolchain. - Each
Clienthas a dedicated pipeline connection pool (#4002). This includes failover clients and cluster node clients.Pipeline,TxPipeline, and autopipeline operations use this pool, so they do not compete with regular commands for main-pool connections. The pool is for burst capacity only: it dials only when necessary, an unused pool holds zero connections, and if the pool is full an operation uses the main pool. SetPipelinePoolSize: -1to get the previous single-pool behavior. AutoPipelineOptions.MaxBatchByteshas a default of 128 KiB (before, there was no limit). The default prevents a write/reply deadlock; it is not a throughput control. The buffers of the pipeline pool also have a default of 128 KiB.
AutoPipelineOptions.FullDuplexFastSubmitis removed (#4018). The submit channel is replaced by a queue that takes a full wave of commands in one lock, so the separate fast path is not necessary. Remove the field from your options.Options.ClientSideCacheRefreshRecencyWindowis removed (#4034). Refresh-on-invalidate now reads again every cached entry of an invalidated key and does not look at how recently the entry was read. On a write-heavy keyspace this means refresh traffic across the full resident cache. Remove the field from your options.LocalCache.LRUClock()is removed (#4034). It returned the recency token that the removed recency window used, and has no replacement. Remove calls to it.ClientSideCacheInvalidationBatchWindowserved stale values in beta.1 (#4033). Under load the batcher applied only ~15% of invalidations, and the cache served invalidated values with a ~100% hit rate. 9.23.0 applies all of them. If you used this option on beta.1, upgrade.
Set AutoPipelineOptions.FullDuplex to enable the full-duplex mode of the automatic pipeliner. The default mode sends one batch per round trip. In full-duplex mode the engine holds one pipeline-pool connection, and a writer goroutine and a reader goroutine move the ordered command stream in both directions at the same time. On a high-latency link each command completes in approximately one round-trip time (RTT) on a single connection. On a 50 ms WAN profile: ~389k ops/s at 52 ms p50, against ~207k ops/s at 116 ms on the half-duplex ordered path.
The mode works on both faces of a standalone Client (AutoPipeline() and AsyncAutoPipeline()), and natively on a ClusterClient when routing goes to masters only. On a cluster, one child engine per master sends each command to the node that owns its slot, and follows MOVED / ASK redirects and retryable replies (LOADING, READONLY, ...) through the usual cluster redirect path. With replica routing (ReadOnly, RouteByLatency, or RouteRandomly), the autopipeliner uses the half-duplex shard flushers, which obey the shard picker. Config().FullDuplex reports the mode in effect.
Options:
FullDuplexWindow— the maximum number of commands in flight (backpressure).FullDuplexIdleTimeoutandFullDuplexMaxHold— when the engine returns the held connection to the pool. The pool hooks then do re-authentication and maintenance-notification handoffs.MaxFlushDelay— now also used in full-duplex mode (#4014). The writer waits only when enough commands are in flight, so low-concurrency callers keep the 1×RTT behavior. WithMaxFlushDelay=250µsat 1024 callers: +22% ops/s and −26% p99. The default is 0, so this is opt-in.NumShards— on a standaloneClient, the number of full-duplex engines (#4022). See below.
Pipeline() on the full-duplex connection (#4021). Standalone Client only. Before, ap.Pipeline() used a pooled connection for each Exec. It now submits the batch to the engine as one contiguous run (FDPipelined), so replies come back in submit order, as on a dedicated connection. At 256–1024 callers with 10 commands per Exec: +49% to +52% throughput on one socket instead of ~150. Errors, retries, hooks, and metrics behave like an ordinary pipeline. A batch that cannot use the stream (a blocking command, a per-command read timeout, a diverted command, or a batch larger than the queue) falls back to the ordinary pipeline. TxPipeline stays on a pooled connection, because MULTI/EXEC needs connection affinity. On a ClusterClient, ap.Pipeline() is still the ordinary cluster pipeline. A batch that the queue cannot admit now reserves its slots and waits in a first-come line, so single commands cannot starve a long pipeline (#4057, merged as part of #4021) by @vlady-kotsev.
Several engines on one client (#4022). With FullDuplex, NumShards > 1 now means N engines on one client: one pool, one hook chain, N held connections. Commands route by a hash of their first key, so commands on the same key stay on one connection and keep their order without Unordered. Keyless commands are distributed round-robin. An FDPipelined batch stays on one engine. The pipeline pool must hold at least NumShards connections for each full-duplex autopipeliner. More engines help only under high concurrency: with 10-command pipelines, 8 engines were 30% slower than 1 at 8 callers, equal at about 128 callers, and 2.2× faster at 1024 callers.
The full-duplex path supports blocking commands, Options.Limiter (one admission per written batch), per-command and pipeline hooks, OTel metrics, retry budgets (a NoRetry command is never sent again after it is on the wire), and seamless maintenance handoffs. Limits of the stream model:
- A hook can observe a command, but cannot stop it. A
ProcessHookthat returns without callingnextdoes not cancel the command on the full-duplex path; the command is already queued on the held connection. Run policy or kill-switch hooks on a plain client or on the half-duplex autopipeliner. - Diverted commands are not ordered with the stream. The engine diverts blocking commands, connection-hostile commands, and managed
HIMPORTcommands. On the async face, wait for the result of a diverted command before you submit a command that depends on it. - Single commands on the full-duplex stream do not use the client-side cache. If CSC is enabled, a cacheable command on the stream does not read or write the cache.
- Duration metrics are recorded per reply. A command that fails before it reaches the reader (lease failure, limiter denial, retry exhaustion, close) calls the error callback but records no operation-duration sample.
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
AutoPipelineOptions: &redis.AutoPipelineOptions{
FullDuplex: true, // stream commands on one held connection, ~1 RTT each
},
})
defer rdb.Close()
// Deferred face: calls return immediately; result accessors block until executed.
ap, err := rdb.AsyncAutoPipeline()
if err != nil {
panic(err)
}
cmds := make([]*redis.StatusCmd, 0, 1000)
for i := 0; i < 1000; i++ {
cmds = append(cmds, ap.Set(ctx, fmt.Sprintf("key:%d", i), i, 0))
}
for _, cmd := range cmds {
if err := cmd.Err(); err != nil {
// handle error
}
}
// Or the blocking face — a drop-in Cmdable where each call blocks like a
// plain client while concurrent callers share the full-duplex pipe:
// ap, err := rdb.AutoPipeline()
// val, err := ap.Get(ctx, "key").Result()
Runnable examples: example/autopipeline (all faces), example/autopipeline-fullduplex (blocking face, Pipeline() and FDPipelined), and example/autopipeline-fullduplex-async (async face, Submit, NumShards) (#4061).
Experimental: the auto-pipelining APIs can change in a minor release.
(#4002, #4014, #4018, #4021, #4022) by @ndyakov
Two new functions for the experimental shared-tracking client-side cache. They have no effect unless CSC is enabled (#3989) by @ndyakov:
- Refresh-on-invalidate (
Options.ClientSideCacheRefreshOnInvalidate): when an invalidation push arrives, the client reads every cached entry of that key again in the background, so the next reader does not pay the miss. Each invalidation of a cached key costs one background read.ClientSideCacheInvalidationBatchWindowcollects invalidation-driven deletes into background batches; without it, the connection reader applies each delete inline. - Miss coalescing (
Options.ClientSideCacheCoalesceMisses): concurrent cache misses are pipelined on one tracked connection. Each miss keeps its own per-key command (no rewrite toMGET), so it is safe on a cluster. Each reply is written to the cache with the connection's tracking generation, so the server can invalidate it.
Both options need the built-in cache (ClientSideCacheConfig, or ClientSideCache set to a *LocalCache). With a custom Cache implementation they are ignored.
The cache read path is also faster (#4034, #4035, #4036, #4037) by @ndyakov. A hit no longer writes a timestamp to the entry; a second-chance bit marks reads, and is set only under eviction pressure. The cache key is built in pooled scratch memory, the key list is built only on a miss, and nil/status/bulk-string hits are decoded without allocation. Against 9.23.0-beta.1 on a warm cache: +18% to +57% reads/s from 4 to 1024 callers, p50 from 2.6–5.4 µs to 1.0–1.5 µs, with identical hit rates and 100% of invalidations applied.
RouteByLatency selects the node with the strictly minimum latency. The measurement has noise (the mean of ten pings, refreshed at most every 10 s), so all clients can select the same node from a set of nodes with almost equal latency. In one production system, GET rates across five replicas in one availability zone had a 590× spread.
The new ClusterOptions.RouteByLatencyTolerance widens the selection to every node within the tolerance of the fastest node, and distributes reads across them round-robin with the ShardPicker. A node in a different availability zone stays outside a sensible tolerance, so zone locality is kept. The default is zero (strict minimum). The option is also on FailoverOptions, for clients from NewFailoverClusterClient; the plain NewFailoverClient does not support latency routing. (#3973) by @jozenstar
The same work fixed a routing defect: the client recorded the nearest healthy node only when that node was also the fastest overall. A node that fails fast (for example, a refused connection) hid every healthy node, and reads went to the failing node. The client now records the healthy minimum separately. (#3994) by @jozenstar
Client.Conn() returns its connection to the parent pool on Close. If you ran AUTH or SELECT on it, later commands on the pooled client run as that ACL user and in that DB. The new Client.EphemeralConn() returns the same sticky connection, but Close removes it from the pool instead. The cost is one dial per EphemeralConn. Client.Conn() is unchanged. (#4039) by @saddamr3e
- Full-duplex auto-pipelining:
AutoPipelineOptions.FullDuplex, withFullDuplexWindow/FullDuplexIdleTimeout/FullDuplexMaxHold, on standalone and cluster clients (#4002) by @ndyakov Pipeline()on the full-duplex connection:ap.Pipeline()andFDPipelinedsubmit a batch to the engine as one contiguous run, on a standaloneClient(#4021) by @ndyakov- Several full-duplex engines on one client:
NumShards > 1withFullDuplexon a standaloneClient, with key-hash routing (#4022) by @ndyakov AutoPipelineOptions.MaxQueuedCommands: an opt-in hard limit on accepted, not-yet-completed autopipeline commands. At the limit a command fails immediately withErrAutoPipelineQueueFull, so memory does not grow without limit when the server is slow. WithFullDuplex, the ordered stream is bounded byFullDuplexWindowinstead (a full window blocks the submitter, it does not reject), andMaxQueuedCommandslimits only the commands that run outside the stream (blocking, connection-hostile,Do). The default0means no limit (#4070) by @ndyakov- Dedicated pipeline pool by default:
PipelinePoolSizehas a default ofDefaultPipelinePoolSize(10) on each client. If the pool is full, an operation uses the main pool. Set-1to disable the pool (#4002, #3959) by @ndyakov - CSC refresh-on-invalidate and miss coalescing:
Options.ClientSideCacheRefreshOnInvalidate(withClientSideCacheInvalidationBatchWindow) andOptions.ClientSideCacheCoalesceMisses(#3989) by @ndyakov Client.EphemeralConn: a sticky connection that is removed from the pool onClose, for session-scopedAUTH/SELECTwork (#4039) by @saddamr3eRouteByLatencyTolerance: distributes reads across nodes with almost equal latency, on cluster clients and onNewFailoverClusterClient(#3973) by @jozenstarAutoPipeliner.WaitClosed: blocks until the drain of accepted commands completes, and returns the drain result. Use it in a wrapper that must not close shared pools during a flush (#3998) by @ndyakovredisotel-nativeWithRecordNilErrors: opt in to countredis.Nilreplies as client errors (see the fix below) (#4025) by @lazergCMSInfo.CellSize: the cell-size field ofCMS.INFOin Redis 8.12 (#4010) by @elena-kolevska
- CSC invalidation batching:
ClientSideCacheInvalidationBatchWindowapplied only ~15% of invalidations under load, so the cache served stale values with a ~100% hit rate. Each batch now takes each shard lock once, and 100% of invalidations are applied (#4033) by @ndyakov - Pool waiter wake-ups:
Putdid not wake a worker that waited for a connection to becomeIDLE, so a streaming-credentials re-auth could stall untilPoolTimeoutand then close the connection (#4028) by @avitenzer. Two related waiter-queue races are fixed: a failed transition left a dead waiter queued, which later moved a connection toUNUSABLEfor nobody (#4072) by @ndyakov - Command key positions: 16 typed commands (
BitOp*,MIGRATE,OBJECT ENCODING/FREQ/IDLETIME/REFCOUNT,XINFO STREAM/GROUPS/CONSUMERS,LMPOP,BLMPOP,SINTERCARD,ZINTERCARD,ZMPOP,BZMPOP) routed by the wrong argument. On aRingthey went to the wrong shard; on a cluster they cost an extraMOVEDround trip. A rawFCALL/FCALL_ROhashed the function name. All now route by their key (#4048) by @ndyakov. RawXGROUP,ZUNION/ZINTER/ZDIFF,SDIFFCARD/SUNIONCARD,TS.NRANGE/TS.NREVRANGE, andHIMPORT SETforms are also fixed (#4022) - Millisecond expirations:
PExpireAt,HPExpireAt, andHPExpireAtWithArgsoverflowed for dates outside the nanosecond range (for example, year 2270), so Redis deleted the key or rejected the command (#4026) by @jakezwang.SetArgs.ExpireAtnow sendsPXATwhen the deadline has millisecond precision; before,EXATdropped the milliseconds (#4053) by @LindseyZ1205 - Cluster
-1timeouts: a-1read/write timeout onClusterOptionsgave node clients the 5 s default instead of no timeout (fixes #4049) (#4050) by @lazerg - OTel
redis.Nilas error: a cache miss (redis.Nil) incrementedredis.client.errorsand was tagged as a server error ondb.client.operation.duration. Nil replies are now classifiedNILand not counted, unlessWithRecordNilErrors(true)is set (fixes #4024) (#4025) by @lazerg - Credential redaction:
SENTINEL SET ... auth-passis now redacted in command rendering (used byredisotel/rediscensusspan attributes), and aMIGRATEpassword with the literal valueauth/auth2is redacted at the correct position (#4011) by @saddamr3e - Min/max aggregator panic: a non-numeric shard reply made
AggMinAggregator/AggMaxAggregatorpanic instead of returning an error fromResult()(#4056) by @Suselz ParseURLskip_verify: an invalid value (for example,skip_verify=yes) was silently treated asfalse.ParseURLandParseFailoverURLnow return an error, like for the other boolean options (#4064) by @orinnzuintptrarguments:uintptrand*uintptrare now encoded like the other unsigned integers; a nil*uintptris encoded as0(fixes #3100) (#4054) by @yamaankhan20- Cluster read routing: the client records the nearest healthy node separately from the overall minimum, so a node that fails fast does not hide the healthy nodes (#3994) by @jozenstar
- Probabilistic
*.INFOforward compatibility: theBF.INFO/CF.INFO/CMS.INFO/TOPK.INFO/TDIGEST.INFOparsers skip unknown fields instead of returning an error. Redis 8.12 addscell sizetoCMS.INFO(#4010) by @elena-kolevska NewClientpanic leak: a panic during construction (for example, a maintnotifications failure inModeEnabled) does not leak the connection pools that already exist, and a typed-nil pool cannot hide the initial panic (#4003) by @ndyakov. The same guards apply toNewFailoverClient(#4002)- File-descriptor leak on rejected connections:
Conn.Closedoes the socket teardown and the unsubscribe/CSC callbacks when the connection is alreadyCLOSED. Before, init and auth failures leaked open descriptors (fixes #3982) (#3985) by @ndyakov - Global logger races: atomics protect the global
LoggerandLogLevel, and call-site attribution is correct again (#3988) by @saddamr3e Conn.onClosedata race: the close hooks that init installs (onCloseandonCscClose) are atomic against a concurrentClose(#3966) by @saddamr3e- Reply-parser hardening: the reply parsers accept zero-length entry arrays (#3995), and
FTHybridCmdreads the full RESP3 map reply without desynchronizing the connection (#3956) by @saddamr3e CLIENT INFOforward compatibility: the parser skips unknown client-flag characters instead of failing the reply (#3977) by @ndyakovGEOSEARCHduplicate args: the command no longer sends duplicate arguments (#3955) by @mehmettokgozMSetEXcluster routing: the constructor sets the first-key position, so typed calls go to the correct slot (#3984) by @shivamrustagi- Maintenance notifications: no endpoint DNS detection when the mode is disabled (#3969) by @Phalanyx
- Autopipeliner
Close: a concurrentClosedoes not block (no re-entrant deadlock), andWaitClosedreturns the drain result (#3998) by @ndyakov - Buffered-push log noise: the buffered-push-data notice in
isHealthyConnis logged only at debug level, so CSC invalidations do not fill the log (#3948) by @ndyakov - Sentinel teardown order: close hooks run in LIFO order, so an autopipeliner drain completes before Sentinel discovery stops, and a closed failover client cannot recreate its Sentinel resources from a late dial (#4002) by @ndyakov
- Pipeline desync containment: if a pre-write push-notification drain fails or a command encoder panics, the
Pipeline/TxPipelinepath removes the connection instead of returning a desynchronized connection to the pool (#4002) by @ndyakov
- CSC read path: second-chance read bit, pooled cache-key scratch, key list built only on a miss, allocation-free decode of simple hits. +18% to +57% reads/s against 9.23.0-beta.1 on a warm cache (#4034, #4035, #4036, #4037) by @ndyakov
- Full-duplex reads and submit queue: replies already in the buffer are read as one group (one read deadline per group, not per reply), and a mutex+slice queue replaces the submit channel, so the writer takes a whole wave in one lock (#4018) by @ndyakov
- Full-duplex
MaxFlushDelay: opt-in write coalescing under load. With 250 µs at 1024–2048 callers: +22% to +25% ops/s and −26% to −29% p99; 30% less CPU at 256 callers (#4014) by @ndyakov - Full-duplex allocations: a ring buffer holds the in-flight queue, and the blocking face uses a pool of batches. 770 B/op → 353 B/op at 2048 concurrent callers (#3970, part of #4002) by @ndyakov
- Zero-copy scan:
Scangets zero-copy semantics, and the RESP reader skips unnecessary data conversions (#3972) by @vlady-kotsev - Autopipeline straggler hold: the engine limits the hold on queued commands when the pipeline pool has a free connection. Uncached p95 on a 50 ms link: 111 ms → 65 ms; real-WAN uncached p99: 314 ms → 177 ms (#3962) by @ndyakov
- Go 1.26 and
govulncheckdependency bumps: the minimum Go version is 1.26, CI defaults to 1.26, and a newgo-versionsjob builds the test matrix fromgo.modplusoldstable/stable(#4060) by @vlady-kotsev - Fast skip gates: tests do a TCP probe of each address before the
Pinggate, which removes ~1.6 min of dial-retry waits when the full stack is not running (#4001) by @ndyakov - Redis Enterprise coverage: the autopipeline suites connect to the RE database and use the suite DB (#3976, #3975), timing assertions scale to the measured RTT (#3978), and the
CLIENT INFOtracking-flag assertion is skipped behind the RE proxy (#3981) by @ndyakov - Docs:
UniversalOptionsdocumentsReadTimeout/WriteTimeout/ContextTimeoutEnabled(#4047) by @lazerg; the README describesDisableIdentityas client identification (CLIENT SETINFO), not identity verification (#4059) by @FanWu-ai - Security policy: send vulnerability reports to the Redis VDP (#3949) by @ndyakov
We thank all the contributors who worked on this release!
@avitenzer, @elena-kolevska, @FanWu-ai, @jakezwang, @jozenstar, @lazerg, @LindseyZ1205, @mehmettokgoz, @ndyakov, @orinnz, @Phalanyx, @saddamr3e, @shivamrustagi, @Suselz, @vlady-kotsev, @yamaankhan20
Full Changelog: https://github.com/redis/go-redis/compare/v9.22.0...v9.23.0
v1.5.0
Full Changelog can be found in CHANGELOG.md. Please read its BEHAVIOR CHANGES section before upgrading.
- @PauliusLozys made their first contribution in https://github.com/shopspring/decimal/pull/403
- @JohnAD made their first contribution in https://github.com/shopspring/decimal/pull/389
- @jtwatson made their first contribution in https://github.com/shopspring/decimal/pull/395
- @c-tonneslan made their first contribution in https://github.com/shopspring/decimal/pull/425
- @Streppel made their first contribution in https://github.com/shopspring/decimal/pull/414
- @orinnz made their first contribution in https://github.com/shopspring/decimal/pull/438
- @amitmishra11 made their first contribution in https://github.com/shopspring/decimal/pull/426
- @SashaMIT made their first contribution in https://github.com/shopspring/decimal/pull/437
- @jakezwang made their first contribution in https://github.com/shopspring/decimal/pull/433
v1.75.0
v1.75.0 improves HTTP parsing and serialization, URI processing, cached file serving, and client connection handling. It also fixes cookie injection, request/response framing problems, and excessive CPU or memory consumption from certain inputs.
Go 1.26 or newer is now required. #2395
- HTTP parsing: request headers are parsed and validated in one pass, with common methods and HTTP/1.1 recognized directly. Header values, case conversion, host validation, and control-byte checks process multiple bytes at a time. The parser also avoids repeatedly scanning the same incomplete header block when data arrives in small reads. #2413, #2414, #2428, #2423, #2412, #2438
- HTTP writing: fewer header and response copies, cached serialized
ServerandDatelines, faster HTTP date formatting, and fewer flushes around chunked trailers. Large buffered responses can usewritevon TCP and Unix connections; TLS and streamed bodies retain the buffered path. #2425, #2415, #2434, #2426, #2433 - URI processing: memoized authority parsing, a fast path for
/, faster quoting and validation, and linear-time path normalization. #2411, #2423, #2453 - Static files: cached files of up to 8 KiB can be served from memory, avoiding a file read on each cache hit and releasing the file descriptor. The new small-file content cache is bounded to 4096 files, or at most 32 MiB per
FS; the existing cache metadata is separate. Changes to these files become visible after cache expiration (CacheDuration, 10 seconds by default). #2446 - Client and server overhead: clients avoid redundant deadline updates when no timeout is active. The opt-in
Server.LazyRequestTimeavoids reading the clock untilRequestCtx.Time()is called; with this option, the returned time is the time of the first call. The benchmark comparison below uses the default setting. #2439, #2404
- Streamed request bodies are drained before connection reuse. With
StreamRequestBodyenabled, the server drains unread body data before reusing the connection, with a 256 KiB drain limit, or closes the connection when safe draining is not possible. Responses are flushed before draining so early replies reach clients still uploading, and response streams can consume the request body before its remaining size is checked. #2368, #2461 - Rejected
100-continuerequests close the connection. A denied request could previously leave its body unread and have those bytes interpreted as the next request, desynchronizing a reused connection. #2450 - Cookie setters prevent additional-cookie injection.
RequestHeader.SetCookieand its byte variants now sanitize semicolons in names and values, so one logical cookie cannot become multiple cookies on the wire. #2393 - Bounded zstd decoding limits decoder memory demand as well as output. A small compressed frame could previously declare a large decoder window even when a decoded-body limit was configured. Positive
BodyUnzstdWithLimitlimits now select a memory-limited decoder, with caps rounded up in 8 MiB buckets; exceeding the decoder limit returnsErrBodyTooLarge. #2407 - Path normalization avoids quadratic work. Repeated separators and dot segments are processed in linear time, reducing the CPU amplification possible with crafted request targets. Incomplete headers arriving one byte at a time also avoid repeated full-block scans. #2453, #2413
- Bodyless responses preserve framing. HEAD and status-defined bodyless responses no longer emit trailer bytes that can be mistaken for the next response. Serialization suppresses forbidden
Content-LengthandTransfer-Encodingon 1xx/204 responses. Explicit content-type metadata and permitted 304 metadata remain supported. #2441, #2447, #2457 - Pipeline clients gain a response-body limit and better failure cleanup. Set the new
PipelineClient.MaxResponseBodySizeto a positive value to reject oversized responses, including withResponse.StreamBody; the default remains unlimited. Idle workers now retire after errors, abandoned requests release their streams, and failed connection establishment is throttled. #2427, #2458
Argument sorting now honors any negative comparator result in Args.Sort and Args.SortKeys, and static-file serving rejects zero-length suffix byte ranges (bytes=-0) with 416 Range Not Satisfiable. #2459, #2460
Other significant changes include opt-in ETags for FS, a net/http request conversion function, improved adaptor handling of flushes, aborts and trailers, and fixes to shutdown and per-IP connection lifetimes. #2399, #2104, #2429, #2445, #2352, #2402
Compared v1.74.0 with master 6141d3a on Ubuntu 26.04, an AMD EPYC 9454, and Go 1.27.1. Each benchmark has ten samples per revision, run alternately with the same inputs and CPU affinity. Negative percentages mean less time per operation. All rows below have p < 0.001.
| Benchmark | v1.74.0 | Release candidate | Time/op change |
|---|---|---|---|
| Minimal GET, pipeline depth 64 | 484.30 ns | 278.05 ns | -42.6% |
| Browser-shaped GET, pipeline depth 16 | 1.080 µs | 722.55 ns | -33.1% |
| Request-header parsing | 561.80 ns | 348.15 ns | -38.0% |
| Response-header parsing | 466.95 ns | 277.55 ns | -40.6% |
| Request-header writing | 49.84 ns | 37.45 ns | -24.8% |
| Response-header writing | 68.64 ns | 40.99 ns | -40.3% |
| Cached 1 KiB file, directory | 2.324 µs | 611.85 ns | -73.7% |
Cached 1 KiB file, os.DirFS |
3.805 µs | 612.30 ns | -83.9% |
| URI path with query string | 91.39 ns | 46.06 ns | -49.6% |
| HTTP date formatting | 108.25 ns | 28.59 ns | -73.6% |
| Trickled ~4 KiB headers, one byte per read | 1385.784 µs | 191.257 µs | -86.2% |
8 KiB path of repeated / |
352.529 µs | 54.330 µs | -84.6% |
~8 KiB path of repeated /a/.. |
96.733 µs | 22.253 µs | -77.0% |
| TCP client/server round trips, small response | 5.193 µs | 5.030 µs | -3.1% |
| TCP client/server round trips, 64 KiB response | 15.302 µs | 13.478 µs | -11.9% |
The pipelined serving and file benchmarks use an in-memory connection with writes discarded. The two TCP rows include real loopback networking. These results describe the selected workloads; TCP gains are smaller than the gains in parsing and serialization. Server.LazyRequestTime remains disabled in this comparison.
- Require Go 1.26 for golang.org/x by @erikdubbelboer in https://github.com/valyala/fasthttp/pull/2395
- discard unread streamed request body before the next request by @alhudz in https://github.com/valyala/fasthttp/pull/2368
- close the connection when ContinueHandler denies a request by @alhudz in https://github.com/valyala/fasthttp/pull/2450
- limit zstd decoder memory in WithLimit methods by @erikdubbelboer in https://github.com/valyala/fasthttp/pull/2407
- Strip semicolons in RequestHeader.SetCookie by @youdie006 in https://github.com/valyala/fasthttp/pull/2393
- make normalizePath linear in the length of the path by @alhudz in https://github.com/valyala/fasthttp/pull/2453
- Omit the trailer section from a response without a body by @h2zi in https://github.com/valyala/fasthttp/pull/2441
- Omit Transfer-Encoding on 1xx and 204 responses by @davidscottpope-gif in https://github.com/valyala/fasthttp/pull/2457
- Omit Content-Length/Content-Type on a parsed bodyless response when re-serializing by @januththedev in https://github.com/valyala/fasthttp/pull/2447
- Add MaxResponseBodySize to PipelineClient by @erikdubbelboer in https://github.com/valyala/fasthttp/pull/2427
- fix: stop idle PipelineClient workers after errors by @erikdubbelboer in https://github.com/valyala/fasthttp/pull/2458
- perf: parse request headers in one validating pass by @h2zi in https://github.com/valyala/fasthttp/pull/2413
- perf: scan the request header block once while parsing by @gaby in https://github.com/valyala/fasthttp/pull/2424
- perf: serve small cached files from memory in FS by @gaby in https://github.com/valyala/fasthttp/pull/2446
- perf: serialize responses with fewer copies and send large ones with writev by @h2zi in https://github.com/valyala/fasthttp/pull/2415
- perf: cut redundant copies out of header serialization by @gaby in https://github.com/valyala/fasthttp/pull/2425
- perf: scan header values, control bytes and case eight bytes at a time by @gaby in https://github.com/valyala/fasthttp/pull/2428
- perf: speed up host validation and query/path quoting- #154 by @gaby in https://github.com/valyala/fasthttp/pull/2423
- perf: memoize authority parsing and fast-path the root path by @h2zi in https://github.com/valyala/fasthttp/pull/2411
- perf: recognize GET, POST, HEAD and HTTP/1.1 without byte-wise validation by @h2zi in https://github.com/valyala/fasthttp/pull/2414
- perf: scan the request target for control bytes a word at a time by @h2zi in https://github.com/valyala/fasthttp/pull/2412
- perf: skip redundant deadline updates on client connections by @gaby in https://github.com/valyala/fasthttp/pull/2439
- perf: preform the Date header line once a second by @h2zi in https://github.com/valyala/fasthttp/pull/2434
- perf: format HTTP dates without time.AppendFormat by @gaby in https://github.com/valyala/fasthttp/pull/2426
- perf: keep the last chunk buffered for the trailer section by @h2zi in https://github.com/valyala/fasthttp/pull/2433
- perf: keep the control-byte scans inlinable by @h2zi in https://github.com/valyala/fasthttp/pull/2438
- fix: request time left at zero, and an opt in Server.LazyRequestTime by @ReneWerner87 in https://github.com/valyala/fasthttp/pull/2404
- Add opt-in ETag support to FS by @anandghegde in https://github.com/valyala/fasthttp/pull/2399
- Add ConvertNetHttpRequestToFastHttpRequest adaptor function by @aaydin-tr in https://github.com/valyala/fasthttp/pull/2104
- fix: preserve response behavior while draining streamed requests by @erikdubbelboer in https://github.com/valyala/fasthttp/pull/2461
- fix: per-IP wrapper lifetime, TLS state unwrapping and ConnState timing by @h2zi in https://github.com/valyala/fasthttp/pull/2352
- fix: carry net/http handler contracts through the fasthttpadaptor by @h2zi in https://github.com/valyala/fasthttp/pull/2429
- Carry response trailers through the fasthttpadaptor by @h2zi in https://github.com/valyala/fasthttp/pull/2445
- fix: write trailers for buffered bodies in Request.Write and Response.Write by @aaydin-tr in https://github.com/valyala/fasthttp/pull/2378
- Start compressing a streamed body when the server reads it by @h2zi in https://github.com/valyala/fasthttp/pull/2442
- fix: synchronize RequestCtx.Done() with ShutdownWithContext by @cpsc in https://github.com/valyala/fasthttp/pull/2402
- fix: admit TimeoutHandler requests on servers driven by ServeConn by @h2zi in https://github.com/valyala/fasthttp/pull/2431
- fix: serve queued waiters when SetMaxConns grows the limit by @h2zi in https://github.com/valyala/fasthttp/pull/2430
- fix: recheck idle connections before queuing waiters by @HarveyBase in https://github.com/valyala/fasthttp/pull/2394
- fix: recognize case-insensitive and multi-value Connection: close (#2418) by @littfed in https://github.com/valyala/fasthttp/pull/2420
- fix: preserve zero Max-Age when parsing cookies by @jakezwang in https://github.com/valyala/fasthttp/pull/2405
- fix: accept a negative Max-Age when parsing cookies by @SulimanAbdulrazzaq in https://github.com/valyala/fasthttp/pull/2416
- Preserve NoDefaultContentType across client response resets by @CSXizhang in https://github.com/valyala/fasthttp/pull/2455
- fix(fs): reject zero-length suffix byte ranges by @kevin9327 in https://github.com/valyala/fasthttp/pull/2460
- fix(args): use comparator sign in Sort and SortKeys by @sergioperezcheco in https://github.com/valyala/fasthttp/pull/2459
- fix: preserve redirect path normalization setting in Request.CopyTo by @cuishuang in https://github.com/valyala/fasthttp/pull/2389
- fix: normalize a trailing /. path segment by @sachhg in https://github.com/valyala/fasthttp/pull/2392
- fix: mask ConnRequestNum into its 32-bit RequestCtx.ID field by @h2zi in https://github.com/valyala/fasthttp/pull/2432
- Return no values from PeekAll for absent special headers by @youdie006 in https://github.com/valyala/fasthttp/pull/2401
- Leave an empty upfront Server or User-Agent out of PeekAll by @h2zi in https://github.com/valyala/fasthttp/pull/2443
- Skip RFC-valid empty elements in VisitHeaderParams by @vzer200 in https://github.com/valyala/fasthttp/pull/2388
- fix(request): warn when Request.Body() is called on streamed requests by @VedantMadane in https://github.com/valyala/fasthttp/pull/2350
- docs: show how to read a bounded response prefix by @fzlzjerry in https://github.com/valyala/fasthttp/pull/2369
- docs: clarify that ShutdownWithContext error does not freeze the Server by @hazyhaar in https://github.com/valyala/fasthttp/pull/2422
- test: add benchmarks for pipelined and browser-shaped serving by @h2zi in https://github.com/valyala/fasthttp/pull/2409
- test: scale hard-coded deadlines with testTimeout by @h2zi in https://github.com/valyala/fasthttp/pull/2410
- Update golangci-lint to v2.13.2 and get rid of the separate gosec action by @erikdubbelboer in https://github.com/valyala/fasthttp/pull/2386
- chore(deps): bump github.com/klauspost/compress from 1.20.0 to 1.20.1 by @dependabot[bot] in https://github.com/valyala/fasthttp/pull/2448
- chore(deps): bump golang.org/x/net from 0.58.0 to 0.59.0 by @dependabot[bot] in https://github.com/valyala/fasthttp/pull/2397
- chore(deps): bump github.com/molecule-man/go-brrr from 1.0.1 to 1.1.1 by @dependabot[bot] in https://github.com/valyala/fasthttp/pull/2417
- @vzer200 made their first contribution in https://github.com/valyala/fasthttp/pull/2388
- @youdie006 made their first contribution in https://github.com/valyala/fasthttp/pull/2393
- @aaydin-tr made their first contribution in https://github.com/valyala/fasthttp/pull/2378
- @sachhg made their first contribution in https://github.com/valyala/fasthttp/pull/2392
- @cpsc made their first contribution in https://github.com/valyala/fasthttp/pull/2402
- @jakezwang made their first contribution in https://github.com/valyala/fasthttp/pull/2405
- @SulimanAbdulrazzaq made their first contribution in https://github.com/valyala/fasthttp/pull/2416
- @HarveyBase made their first contribution in https://github.com/valyala/fasthttp/pull/2394
- @anandghegde made their first contribution in https://github.com/valyala/fasthttp/pull/2399
- @littfed made their first contribution in https://github.com/valyala/fasthttp/pull/2420
- @januththedev made their first contribution in https://github.com/valyala/fasthttp/pull/2447
- @hazyhaar made their first contribution in https://github.com/valyala/fasthttp/pull/2422
- @CSXizhang made their first contribution in https://github.com/valyala/fasthttp/pull/2455
- @VedantMadane made their first contribution in https://github.com/valyala/fasthttp/pull/2350
- @davidscottpope-gif made their first contribution in https://github.com/valyala/fasthttp/pull/2457
- @sergioperezcheco made their first contribution in https://github.com/valyala/fasthttp/pull/2459
- @kevin9327 made their first contribution in https://github.com/valyala/fasthttp/pull/2460
Full Changelog: https://github.com/valyala/fasthttp/compare/v1.74.0...v1.75.0
v0.9.1
- build(deps): bump golang.org/x/sync from 0.20.0 to 0.22.0 by @dependabot[bot] in https://github.com/andydunstall/piko/pull/317
- build(deps): bump github.com/stretchr/testify from 1.11.1 to 1.12.1 by @dependabot[bot] in https://github.com/andydunstall/piko/pull/321
- build(deps): bump github.com/MicahParks/keyfunc/v3 from 3.8.0 to 3.8.1 by @dependabot[bot] in https://github.com/andydunstall/piko/pull/319
- build(deps): bump github.com/prometheus/client_golang from 1.23.2 to 1.24.1 by @dependabot[bot] in https://github.com/andydunstall/piko/pull/318
- build(deps): bump github.com/ugorji/go/codec from 1.3.1 to 1.3.2 by @dependabot[bot] in https://github.com/andydunstall/piko/pull/320
- build(deps): bump go.uber.org/atomic from 1.11.0 to 1.12.0 by @dependabot[bot] in https://github.com/andydunstall/piko/pull/323
- build(deps): bump github.com/MicahParks/keyfunc/v3 from 3.8.1 to 3.8.2 by @dependabot[bot] in https://github.com/andydunstall/piko/pull/324
- build(deps): bump github.com/go-jose/go-jose/v4 from 4.1.4 to 4.1.5 by @dependabot[bot] in https://github.com/andydunstall/piko/pull/325
- chore: omit unreachable nodes from digest to stop failed nodes being resurrected by @miledxz in https://github.com/andydunstall/piko/pull/327
- @miledxz made their first contribution in https://github.com/andydunstall/piko/pull/327
Full Changelog: https://github.com/andydunstall/piko/compare/v0.10.0...v0.9.1
v0.16.1
Security and stability update with no public API changes. Requires Go 1.26 or later.
This release fixes a denial-of-service vulnerability in grayscale and Indexed image rendering, including CalRGB palette handling. Processing a crafted PDF could cause an index-out-of-range panic and terminate the calling process if the panic was not recovered.
Versions v0.16.0 and earlier are affected. Applications extracting or rendering images from untrusted PDFs should upgrade to v0.16.1. See GHSA-6524-w46v-6399.
Improvements:
- Harden PDF parsing, image processing, JSON input handling, and signature-data parsing.
- Prevent panics when rendering grayscale and Indexed images, including palette bounds and Decode mapping checks.
- Strengthen external-link checks and reporting of problematic links; redact URL passwords in reports and CLI output.
- Improve structural validation of embedded files, 3D annotations, and multimedia dictionaries.
- Report JavaScript and supported media presence during validation without executing content or displaying script code.
- Report invalid zero-offset xref entries as skipped in relaxed validation and reject them in strict mode (#612).
- Fix strict validation of PDF dates ending in
Z(#1493). - Update the TIFF dependency to v1.0.7.