v0.15.0
This release is a focused follow-up to v0.14.0 improving diagnostics and observability for validating large PDF corpora. It also includes PDF processing hardening, CJK text wrapping for watermarks, and clearer signature-validation behavior.
v0.15.0 follows v0.14.0 by only one week because post-release corpus testing made an existing operational gap clear: large wildcard-driven validation runs delayed failure diagnostics until the full input set completed and provided no quiet-mode indication of the file currently being processed.
The changes are targeted and immediately useful to corpus testers, so holding them for a later feature release would unnecessarily delay feedback. The short interval is intentional and does not establish a weekly release routine.
Multi-file CLI validation now reports each failed input as soon as the failure is detected, continues with the remaining inputs, and exits nonzero with a compact summary:
validation failed: 66 of 463 files invalid
The new --progress flag identifies the active input during quiet validation runs. This is useful when a long-running corpus job stalls on a particular file:
pdfcpu validate -q --progress "**/*.pdf"
The quotes are intentional. They pass the recursive pattern to pdfcpu for internal expansion instead of asking the shell to expand thousands of paths into one command line, which may exceed the operating system's argument-size limit.
With -q --progress, progress and validation failures are written to standard error while standard output remains clean:
validating(mode=relaxed) path/to/input.pdf ...
validate path/to/input.pdf: validation error: ...
There is a new Corpus Validation guide documenting quick assessment, progress monitoring, logging, validation modes, result interpretation, and focused issue reporting.
A failed corpus run is diagnostic input, not an issue backlog. Public reports must isolate one manually verified and independently reproduced problem with the smallest shareable PDF. Include the relevant error and final summary, and attach large logs as compressed files instead of pasting them into an issue.
AI-generated, bulk-generated, or mechanically reformatted corpus reports will be closed immediately without investigation. Corpus-wide analysis, failure classification, confidential-file investigation, and scheduled remediation are separate engineering work and may require a paid engagement.
Limited corpus investigation and remediation work may be available by arrangement.
Malformed and inconsistent PDF structures now produce more contextual errors across reading, dereferencing, validation, optimization, fonts, forms, and XObjects. Integer conversion and encryption handling have been tightened, and additional guards reduce the risk of panics while processing damaged input.
This work is backed by expanded malformed-input, error-path, and optimization regression coverage.
Text watermarks now support automatic wrapping for CJK text, including long runs without spaces. Oversized text is wrapped to the configured width instead of forcing unintended font-size reduction. This resolves issue #1427 and includes expanded CJK wrapping and layout coverage.
Signature validation output is clearer for legacy and unsupported cases. Evidence handling, PKCS#1 and PKCS#7 processing, and related error classification have also been tightened.
The pkg/api multi-file validation contract is unchanged: API callers continue to receive joined errors after processing. The CLI now streams individual multi-file validation failures and returns a final summary, so scripts that compare complete stderr strings should be updated to rely on exit status and stable error classification instead.
Existing non-quiet validation already reports the active input. --progress extends that visibility to quiet corpus runs without duplicating normal progress output.
- f2686555086a2e76dc19f602ea1897f6e3baae4d bump version
- dcc168d28225f7f3266836668483a3cdaced729a update issue reporting guidelines
- f5f3edf37f9642c4403300db88109521085006f2 harden PDF validation and add progress reporting
- afc357e23773c32d61b8c18601f77aa8ffcb6dab clarify signature validation output and legacy handling
- c9c07d0fcd19439f967cfff96203ebe41a1e8327 harden crypto integer conversions
- 198382701afee53f783aaf425cc240ee26170ae4 upgrade gh workflows
- 4e5f3aba056aebb721d7334032b378aeba1708b8 harden crypto integer conversions
- b57300b5f79f9139a7a6534fce7cbc96633ae025 clean up for #1427
- a2c33719c6aa55593d0316e50247388a3763b5f2 add CJK auto-wrap support for text watermarks (#1427)
Wails v3.0.0-beta.7
- Add macOS autoplay preference to disable user action requirement for media playback in PR by @Eyalm321
- Move mailbox changelog entry to Unreleased in PR by @leaanthony
- macOS zoom animation uses CADisplayLink or NSTimer for smoother performance in PR by @savely-krasovsky
- Configure iOS Xcode project to retain inherited linker flags and add -ObjC in PR by @mortenolsrud
- Fix excessive TCP connection churn in the
wails3 devasset proxy on large frontends, which could exhaust the host's ephemeral ports and make unrelated processes fail withEADDRNOTAVAIL - Queue per-window event JavaScript for ordered dispatch and backpressure in PR by @leaanthony
🤖 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.7
Wails v3.0.0-beta.6
- Implement bounded host-side storage for oversized events and ordered JavaScript delivery in PR by @leaanthony
- Implement macOS Dock bounce for window flashing in PR by @julianstorer
- Asset server preserves content-type sniffer errors and unwritten prefixes during flushing in PR by @leaanthony
- Prevent macOS applications from crashing when replacing the application menu from a Wails callback
- Fix unreadable native menus on Windows 10 1809 / Windows Server 2019 (build 17763). The dark-mode uxtheme exports were gated on build 18334, so the app-level dark-mode opt-in never ran on those hosts: the menu background was painted dark but Windows kept drawing menu text in the light theme, leaving dark text on a dark background. The ordinals exist from 17763, so the gate now matches.
- Fix
w32.GetStockObjectcallingGetDeviceCapsinstead ofGetStockObject, which made it return 0 for every stock object. - Improve WebView2 bootstrapper download error handling and reporting in PR by @jannskiee
🤖 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.6
Wails v3.0.0-beta.5
- macOS app activation now respects activation policy for regular apps only in PR by @julianstorer
- Guard uninitialized GTK windows in Linux builds in PR by @julianstorer
- Set explicit opaque background color for Linux WebKit windows before URL load in PR by @julianstorer
🤖 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.5
v6.10.0
- fix(website): uglyURLs on main, update documentation by @alex-dna-tech in https://github.com/micro/go-micro/pull/4871
- fix: preserve AtlasCloud conformance markers across text tool fallbacks by @mvanhorn in https://github.com/micro/go-micro/pull/4878
- cmd/micro gateway HTTP->RPC proxy: Endpoint handling via URL parsing is not implemented by @alex-dna-tech in https://github.com/micro/go-micro/pull/4879
- @mvanhorn made their first contribution in https://github.com/micro/go-micro/pull/4878
Full Changelog: https://github.com/micro/go-micro/compare/v6.9.0...v6.10.0
Wails v3.0.0-beta.4
- Android build tasks default to arm64 and deploy-emulator selects host architecture in PR by @mortenolsrud
- Preserve macOS window zoom state during drag and reduce motion in PR by @leaanthony
- Fix Windows server-mode build by adding
!servertowebview_window_windows_nonclient.gobuild constraint
🤖 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.4
v0.14.0
Shared custom font directories (#1454)
Installed font metrics are now world-readable (0644). Calling api.InstallFonts(nil) reloads existing fonts without modifying the font directory, allowing fonts installed by one user to be shared safely with other users that have read-only access.
The github.com/pkg/errors dependency is gone. Error handling has been migrated throughout the codebase to standard Go patterns.
Errors now retain useful operation and input context, and exported sentinel errors make failures easier to classify with errors.Is and errors.As.
Public API boundaries now validate nil and invalid arguments instead of risking panics. Multi-input operations return joined errors where appropriate, while the CLI presents concise errors without exposing stack traces by default.
File operations are safer as well: output is staged before replacement, existing files and permissions are preserved on failure, filesystem aliases and output collisions are detected, and temporary-file cleanup has been hardened across platforms.
CLI users should see clearer and more complete diagnostics, particularly for batch validation, merging, attachments, encryption, certificates, fonts, and page operations.
Other notable changes include:
- JSON output for certificate listing.
- Support for the
OneColumnpage layout. - Optional installed-font selection for
fonts cheatsheet. - Improved attachment glob handling.
- Stricter validation of ordered, unique page split points.
- Clearer certificate-import and replacement behavior.
- Improved reporting when one or more inputs in a batch fail.
The public API now offers more consistent reader/writer and file-based entry points, along with exported errors that callers can inspect without parsing error strings.
New and expanded API support includes:
- Grid operations.
- Raw signature validation.
- Machine-readable certificate listing.
- List-oriented bookmark, box, form-field, image, property, permission, and viewer-preference operations.
- Explicit handling policies for unsupported resources during extraction.
- Transactional certificate and TrueType collection installation.
Callers that compare complete error strings should migrate to errors.Is or errors.As, as many errors now include additional operation and source context.
Signature, timestamp, PKCS#7, certificate-chain, and revocation processing received substantial hardening. This area remains under active development, and further work on signature creation, validation, revocation, and interoperability is ongoing.
Remote image fetching for create and form inputs now rejects private and local destinations, including redirects and DNS results. Access to private revocation endpoints must be explicitly allowed through allowedRevocationHosts.
The external github.com/hhrutter/lzw and github.com/hhrutter/pkcs7 dependencies have been replaced by internal implementations. Remaining dependencies have been updated.
The experimental Windows 7 build is being discontinued because the request for community testing received no response. It relied on an unofficial patched Go toolchain and could not be tested on Windows 7 in CI. Official Go releases require Windows 10 or Windows Server 2016 or later.
This release also includes numerous fixes across parsing, validation, forms, fonts, attachments, merging, page trees, annotations, images, encryption, and digital signatures, backed by substantially expanded regression and error-path coverage.
Many of the fixes in this release began with reports from you - the pdfcpu users. Thanks everyone who opened issues, provided reproducible examples, tested fixes, and helped identify difficult PDF edge cases. Your feedback directly improves pdfcpu's reliability.
- ef9ddad769cc8a3d01a960e5b90b6a1456da2454 bump version
- 8c3a1f531efe9862f2612a788de99c2f6f41ffe0 fix(font): reload existing fonts for empty API input
- 384defd25ee5ce021743bd85266101c93fa94a70 update gh templates
- b3ddbc83c43217220f5ee18d899b80941420a3f7 fix: make installed fonts shared-readable
- 1e88df971c49e98f38ce03f329a52603904a8ed5 Update Readme
- 2f43fd6cc2905173df03e5436da00208e2231a71 fix Windows test portability
- 5f7bf42782d8a66f99a07dc908d899f8c57e9325 bump version
- 029fa541626fa0e57417c162e2d8022d66b7f777 harden recursive action and bead validation
- 5538e8a28362a2a299741a38acc3c9c4ded3f5a8 fix #1383
- 4b6b06b727a2da26edace0cf01e4167ee2c8fa10 fix #1448
- f3c9ed641f4d71cba01d79013b1d4ddc0184b6c5 cleanup error handling
- e7973f530e0c26ef737fbf09fd9d67386fb061a7 harden integer validation and CI permissions
- 4dc0e627dd83b91ae2d18f5c71c4d687158f92d8 fix #1440
- 1fa26614a5d3ef31895e16a9841ae3b0241b18b6 fix #1439
- 0f5d517ad38696bf5f1e8a8629b213b37359d082 fix #1438
- e413baf0ee4d2217c925f2c575f699642735f9f6 fix #1437
- d2488f1ac57a3bf29b74e170c0abffbf92ff2949 fix #1431
- 5a2455464d47d3def7dc1e557d4b39e1bfa5fdd3 cleanup error handling
- 997afda467c0ac1b951afa8e0cb76a77d3ec1b4e hide stack traces from default API and CLI errors
- 29f40fc7931e7ee86740a108edba6964d3f5a2e0 clarify encryption error classification
- 63bf1f77b96cf9b38606d40dcb9f3aaa17848cd9 fix(cli): surface multi-input command errors
- 553aa81578d64639464f937f4304ef2b3410a50d fix(cli): return batch validation and merge source errors
- 73c7c23f26b3a4f12b5f00bcf674d11fc2f5ab64 api: add source context to merge errors
- cd741d7cd6236aff035fc5e6204c5c892401ff4d api: harden error and nil argument handling
- ae3023dea86744ed5daa4889e888000c8a83c3c9 fix #1051
- fd3c42b27a2390c4d08e023752dfc6f82ddc2eb2 add regression test for #1059
- db89772699f8e7c11ae8cb9ed28ad56e591cef0e fix #1088
- 6d12b95a71b821e6f17ac17a53188de42f92f4a0 fix #1091
- 04d6bbe64ad95fc8b870ee82a06648490374557d fix #866
- 75ded47336131eeb1c88832cdf490aa27ae0dade fix #1101
- 85bb38147fadcb427b990f608828cb63fab393b3 fix #1123
- 8231174c537540e2f0e53afa10425a32c4f50018 fix #1127
- b897b5fc2151faeb0e1636a37afc439f7b6f04d1 fix #1161
- c5124d8b968e4edab3a7d8ea5a0db2517f229640 upgrade dependencies
- 6e318a071bfa1b55237693af76cd85c084609804 fix #415
- 7623a909bc0e0a2238e9f7a955986502b5bb5f84 fix #1265
- a35e551de8735de4df863c0169b0016158acda4d add regression test for #1271
- 63ad8b674b94c7e40b137775c2db40b8e3e97980 fix #1279
- 34a477540d47b5c4e66c34cf34c9ce16b58b02e0 Harden remote image fetching for create/form inputs
- ee19119ca7ca1629f790c864773e70f7acf90188 fix #1282
- 5868dc5c717b9326db5332f96d5997e19b23e2c5 fix #1311
- ca9e9aa7599b53919f8339a97fd4ce199842b20f fix #1325
- 85f5d1176d6c88b270e6f3bcd200f8db6995e8d7 fix #1326
- dfb3b495547f0f893c64a2435375aaf96c531698 internalize PDF LZW filter implementation
- 6c60ed40edde055ef6ec65cb125cf8534ae991a4 fix #1302
- def15db189d09ee1ce3d193407929cf479bc0674 fix #1340
- 66f98fd3ef7f8ba7cb290273984f5ba5433462a7 fix #1387
- a496812e7b3273a20ca1f3c9b5767480379e900e fix #1419
- 30b7bc52d63c2d6500c01f45116c77bfef77ee2e fix #1417
- e711196a14d0e8b7998e58d7c367e010b03ba3ef fix #1403
- dbf99078520ccc6b1d4e4f42e09972de4dbaa60d fix #1274
- d79565d9795ad58541a145bdfd717c39cd87d6f2 add regression test for invalid destination stream #933
- 887462d0ebdc0a8b4f5890ab2fc80b0fc165c89e fix #1289
- d5ec9d33375077d7f4d2146fef0534fde0b81178 Add xref stream regression tests for #399 and #401
- 89d4bc11226cd053313cf9b6aa0f796f5139bba7 fix #990
- d01e39156fbe63af408843aee507603771d5f79f fix #1385
- b45031cb2eccfa228ac0d7f8fcbbe85f3843987c Fix tmp file handling & permissions
- b17661e880e6c11136acfaba3515f88ba5274fe9 Add missing guards
- 7bde99e930bac475b1fbcbd8c28b25a209b1009c Fix #1404
9.22.0
This is a minor release introducing two flagship (experimental) features — client-side caching and automatic pipelining — alongside support for Redis 8.10, new commands, and a large batch of stability and parser-robustness fixes. It consolidates everything shipped in 9.22.0-beta.1, so the notes below cover the full 9.21.0 → 9.22.0 upgrade.
- Default configuration values changed (#3918): read/write timeouts, retry backoff, cluster state reload interval, and TCP keep-alive defaults are now aligned with the cross-SDK configuration proposal (see the highlight below). Explicitly configured values are unaffected.
WaitAOFreturn type corrected (#3888):WaitAOFnow returns*IntSliceCmd, matching the two-integer reply ofWAITAOF(previously*IntCmd, which failed to parse the reply at runtime). Code referencing the old return type needs a one-line update.
The standalone Client gains server-assisted client-side caching built on RESP3 CLIENT TRACKING. Enable it by setting ClientSideCacheConfig in Options (or supply your own cache via ClientSideCache — e.g. to share one cache across clients). Cacheable read results are served from a local in-process cache and invalidated automatically when the server reports a change, cutting round trips for read-heavy workloads.
The invalidation architecture is selected by ClientSideCacheStrategy; the default (and currently only) strategy is CSCStrategySharedTracking: one shared cache, every pool connection runs plain CLIENT TRACKING ON, and a background drainer applies buffered invalidations — portable (no BCAST) and consistent with the other Redis client libraries. Requirements and guardrails: RESP3 (Protocol: 3), standalone client, DB 0 only; commands that would change the connection identity (SELECT, AUTH, ...) are rejected while caching is enabled, and CSC is disabled when a credentials provider is set (fixed Username/Password work and are namespaced). See the README's client-side caching section and the runnable example.
Experimental: the API may change in a minor release.
(#3941) by @ofekshenawa
AutoPipeliner is a background batcher that coalesces commands from many concurrent goroutines into Redis pipelines, multiplying throughput without any manual pipeline management. It comes in two faces, available on Client and ClusterClient (and configurable via Options.AutoPipelineOptions / UniversalOptions.AutoPipelineOptions):
AutoPipeline()— the blocking face: a drop-inCmdablewhere each call blocks until executed, exactly like a plain client, while concurrent callers' commands batch together under the hood (measured locally over loopback: ~1M+ SET/sec vs ~100k unpipelined; indicative, not a guarantee). Per-goroutine command order is preserved.AsyncAutoPipeline()— the deferred face: command calls return immediately and every typed result accessor (Val/Result/Err/...) blocks until the command has executed. Submit a window of commands, then read the results, to keep pipelines deep (~2–3M SET/sec locally; indicative).
AutoPipelineOptions controls batching: MaxBatchSize (soft target, default 200; the blocking face's preset uses 300), MaxBatchBytes (approximate payload cap so huge values flush as several bounded writes), MaxFlushDelay with optional AdaptiveDelay (delay scales down as the queue fills), and MaxConcurrentBatches (default 1 = a single ordered batch stream; raising it requires Unordered: true, so ordering is never lost by accident — Validate() rejects the combination otherwise). A usage tour and throughput comparison live in example/autopipeline.
Experimental: the API may change in a future release — pin your go-redis version if you adopt it.
(#3942) by @ndyakov, with help from @cxljs
This release adds support for Redis 8.10. The README's supported-versions list now includes Redis 8.10, and CI runs the full suite against the redislabs/client-libs-test:8.10.0 image by default (#3920, #3940).
Coverage for the new commands and options that ship with Redis 8.10:
HIMPORT(#3919) — bulk hash import via server-side fieldsets, exposed asHImportPrepare,HImportSet,HImportDiscard, andHImportDiscardAll. Fieldsets are session state scoped to a single physical connection, which does not mix well with connection pooling — so the client keeps a versioned fieldset registry and lazily replays thePREPAREon whichever pooled connection executes aSETthat needs it, at most once per connection, with no extra round trip (thePREPAREis injected into the same write as theSET).LMOVEM/BLMOVEM(#3913) — move multiple elements between lists in one call.SUNIONCARD/SDIFFCARD(#3897) — cardinality of set union/difference without materializing the result.XREAD/XREADGROUPMAXCOUNTandMAXSIZE(#3898) — bound how much data a stream read returns.TS.READ(#3896),TS.QUERYLABELS(#3926),TS.NRANGE/TS.NREVRANGE(#3870) with multiple aggregators per key (#3937), andEXCLUDEEMPTYonTS.MRANGE/TS.MREVRANGE(#3912) — new time-series query surface.FT.ALIASLIST(#3925),COLLECTreducer forFT.AGGREGATE(#3886),RERANKon HNSW vector fields inFT.CREATE(#3927), andFT.HYBRIDtimeout warnings (#3911) — search coverage.
Default configuration values now follow the cross-SDK configuration proposal shared by all Redis client libraries (#3918):
| Setting | Old default | New default |
|---|---|---|
ReadTimeout / WriteTimeout |
3s | 5s |
| Retry backoff (min/max) | 8ms / 512ms | 10ms / 1s |
| Cluster state reload interval | 10s | 60s |
| TCP keep-alive | 5min period | 30s idle / 5s interval / 3 probes (net.KeepAliveConfig) |
Applications that set these values explicitly are unaffected; applications relying on the old defaults inherit the new ones.
A systematic audit fixed data races across the client — hooks (AddHook, #3868), Ring.SetAddrs (#3862), cluster node slices (#3861), pub/sub reconnect (#3906), maintenance notifications (#3894, #3872), pool handoff (#3876), and redisotel (#3881) — and hardened the RESP parsers against malformed or unexpected replies: over-reads on nil replies (#3874), integer overflow when skipping map/attribute bodies (#3877), unhashable RESP3 map keys (#3873), odd-length flat replies (#3900), mismatched declared array lengths (#3907), unexpected extra reply frames (#3884), and nil elements in numeric/bool slice replies (#3922).
PeekPushNotificationName blocked until 36 bytes were buffered, so a short subscribe confirmation (channel name of six or fewer characters) on an otherwise idle connection hung PubSub.Receive forever — a regression introduced in 9.20.1 by #3842. The peek now parses whatever is already buffered and only waits for one more byte when the frame prefix is valid but incomplete. Fixes #3935.
The cluster transaction pipeline treated a MULTI...EXEC block as independently retryable commands, which could scatter a transaction across nodes or send malformed transactions on retry. Redirects (MOVED/ASK/TRYAGAIN) and aborts are now handled at the whole-transaction level, matching Redis transaction semantics: the transaction is re-routed and retried as a unit, never partially (#3909) by @cxljs.
rediscmd.AppendCmd — used by redisotel and rediscensus to render commands into span attributes — now redacts credential arguments as <redacted>: AUTH, HELLO ... AUTH, CONFIG SET of requirepass / masterauth / TLS key passphrases, ACL SETUSER password rules, and MIGRATE ... AUTH/AUTH2. The client sends HELLO ... AUTH on every handshake and AUTH on every streaming-credentials rotation through the regular hook chain, so tracing hooks previously captured credentials even when the application never issued an auth command itself (#3939) by @saddamr3e.
- Client-side caching: server-assisted caching for the standalone client via
ClientSideCacheConfig/ClientSideCache, with theCSCStrategySharedTrackinginvalidation strategy (#3941) by @ofekshenawa - Automatic pipelining:
AutoPipeline()(blocking) andAsyncAutoPipeline()(deferred results) onClientandClusterClient, configured viaAutoPipelineOptions(#3942) by @ndyakov, with help from @cxljs HIMPORTcommand family:HImportPrepare/HImportSet/HImportDiscard/HImportDiscardAllwith lazy per-connection fieldset prepare replay (#3919) by @ndyakovLMOVEM/BLMOVEM: move multiple list elements in one call, withCOUNT(up to N) orEXACTLY(all-or-nothing) semantics viaLMoveMArgs(#3913) by @ofekshenawaSUnionCard/SDiffCard: cardinality of set union/difference (#3897) by @ofekshenawaXRead/XReadGroupMAXCOUNT/MAXSIZE: bound stream read responses by entry count or payload size (#3898) by @ofekshenawaTS.READ: read samples from a series starting at a given timestamp, withTSReadEarliest(-),TSReadLatest(+), andTSReadNew($) sentinels (#3896) by @ofekshenawaTS.QUERYLABELS: query label names/values across time series (#3926) by @ndyakovTS.NRANGE/TS.NREVRANGE: range queries across multiple series (#3870) by @ofekshenawa, with multiple aggregators per key (#3937) by @ndyakovTS.MRANGE/TS.MREVRANGEEXCLUDEEMPTY: skip series with no samples in the result (#3912) by @ofekshenawaFT.ALIASLIST: list all index aliases (#3925) by @ndyakovFT.AGGREGATECOLLECTreducer: collect grouped values into an array (#3886) by @ndyakovFT.CREATERERANK:RERANKparameter on HNSW vector field definitions (#3927) by @ofekshenawaFT.HYBRIDtimeout warnings: timeout warnings are now populated in hybrid search results (#3911) by @ofekshenawaFT.HYBRIDKNNSHARD_K_RATIO(Redis 8.8+): per-shard K ratio for KNN clauses (#3841) by @ndyakov
- PubSub
Receivehang: peek push-notification names without demanding 36 buffered bytes, fixing a hang on short subscribe confirmations (fixes #3935, regression from 9.20.1) (#3936) by @ndyakov - Cluster transactions: re-route the whole tx pipeline on redirect/abort instead of per-command (#3909) by @cxljs
- Credential leak in traces:
rediscmd.AppendCmdredacts credential arguments (AUTH,HELLO ... AUTH,CONFIG SETsecret params,ACL SETUSERpassword rules,MIGRATE AUTH/AUTH2), soredisotel/rediscensusspan attributes no longer contain passwords (#3939) by @saddamr3e WaitAOFreturn type: returns*IntSliceCmdmatching the two-integerWAITAOFreply (#3888) by @CipherN9Ring.Publishrouting: publish to the shard that owns the topic instead of a round-robined one (#3893) by @dkindel- Pool
OnRemovehooks: fireOnRemoveonputConneviction paths so removal hooks see every evicted connection (#3932) by @cxljs UniversalClientInfoMap: addedInfoMapto theCmdableinterface (#3904) by @nazarli-shabnamSlowLogGetcontext: pass the caller's context instead of a background one (#3915) by @sonnemuskModuleLoadexnil config: return an error instead of panicking on nil config (#3916) by @sonnemuskParseURLIPv6 hosts: keep single brackets for IPv6 hosts without a port (#3882) by @sueun-devParseURLdurations: treat unit durations<= 0as disabled (#3866) by @sueun-dev- Nil
*uint8encoding: encode nil*uint8as"0"like other numeric pointers (#3869) by @sueun-dev JSONSliceCmdread errors: return the read error fromreadReplyinstead of swallowing it (#3903) by @saddamr3e- RESP parser hardening: reconcile declared entry-array lengths (#3907), handle nil elements in int/uint/bool slice parsers (#3922), drain unexpected reply frames (#3884), reject odd-length flat replies in Z/KeyValue parsers (#3900), avoid int overflow when skipping map/attr bodies (#3877), don't over-read nil replies in
Reader.Discard(#3874) by @saddamr3e; reject unhashable keys in RESP3 map parsing (#3873) by @iabdullah215 - Data races: hook state during
AddHook(#3868),onNewNodeduringRing.SetAddrs(#3862), shared masters/slaves slices in cluster (#3861), sharedopt.Addrduring pub/sub reconnect (#3906),clusterStateReloadCallbackin maintnotifications (#3894), conn reader inisHealthyConnduring handoff (#3876) by @saddamr3e; handoff race window in maintnotifications (#3872) by @ndyakov redisotel: useObservableCounterfor cumulative pool stats (#3914) by @Solaris-star; avoid a data race on shared attributes duringMinIdleConnswarmup (#3881) by @ndyakov
- Cross-SDK default alignment: new defaults for timeouts, retry backoff, cluster state reload, and TCP keep-alive (#3918) by @ndyakov
- CI on Redis 8.10: 8.10 made the default test version (#3920) with version gating by major.minor (#3908) by @ofekshenawa; the test stack now runs the GA
redislabs/client-libs-test:8.10.0image and 8.8 was dropped from the CI matrix (#3940) - Type-safe atomics: use typed
sync/atomicvalue types (#3860) and remove the deadassertUnstableCommandRESP3 path (#3928) by @cxljs - Docs: clarify that
ExpireTime/PExpireTimereturn Unix timestamps (#3917) by @sonnemusk; remove a duplicate example step (#3875) by @andy-stark-redis
We'd like to thank all the contributors who worked on this release!
@andy-stark-redis, @CipherN9, @cxljs, @dkindel, @iabdullah215, @nazarli-shabnam, @ndyakov, @ofekshenawa, @saddamr3e, @Solaris-star, @sonnemusk, @sueun-dev
Full Changelog: https://github.com/redis/go-redis/compare/v9.21.0...v9.22.0
9.22.0
This is a minor release introducing two flagship (experimental) features — client-side caching and automatic pipelining — alongside support for Redis 8.10, new commands, and a large batch of stability and parser-robustness fixes. It consolidates everything shipped in 9.22.0-beta.1, so the notes below cover the full 9.21.0 → 9.22.0 upgrade.
- Default configuration values changed (#3918): read/write timeouts, retry backoff, cluster state reload interval, and TCP keep-alive defaults are now aligned with the cross-SDK configuration proposal (see the highlight below). Explicitly configured values are unaffected.
WaitAOFreturn type corrected (#3888):WaitAOFnow returns*IntSliceCmd, matching the two-integer reply ofWAITAOF(previously*IntCmd, which failed to parse the reply at runtime). Code referencing the old return type needs a one-line update.
The standalone Client gains server-assisted client-side caching built on RESP3 CLIENT TRACKING. Enable it by setting ClientSideCacheConfig in Options (or supply your own cache via ClientSideCache — e.g. to share one cache across clients). Cacheable read results are served from a local in-process cache and invalidated automatically when the server reports a change, cutting round trips for read-heavy workloads.
The invalidation architecture is selected by ClientSideCacheStrategy; the default (and currently only) strategy is CSCStrategySharedTracking: one shared cache, every pool connection runs plain CLIENT TRACKING ON, and a background drainer applies buffered invalidations — portable (no BCAST) and consistent with the other Redis client libraries. Requirements and guardrails: RESP3 (Protocol: 3), standalone client, DB 0 only; commands that would change the connection identity (SELECT, AUTH, ...) are rejected while caching is enabled, and CSC is disabled when a credentials provider is set (fixed Username/Password work and are namespaced). See the README's client-side caching section and the runnable example.
Experimental: the API may change in a minor release.
(#3941) by @ofekshenawa
AutoPipeliner is a background batcher that coalesces commands from many concurrent goroutines into Redis pipelines, multiplying throughput without any manual pipeline management. It comes in two faces, available on Client and ClusterClient (and configurable via Options.AutoPipelineOptions / UniversalOptions.AutoPipelineOptions):
AutoPipeline()— the blocking face: a drop-inCmdablewhere each call blocks until executed, exactly like a plain client, while concurrent callers' commands batch together under the hood (measured locally over loopback: ~1M+ SET/sec vs ~100k unpipelined; indicative, not a guarantee). Per-goroutine command order is preserved.AsyncAutoPipeline()— the deferred face: command calls return immediately and every typed result accessor (Val/Result/Err/...) blocks until the command has executed. Submit a window of commands, then read the results, to keep pipelines deep (~2–3M SET/sec locally; indicative).
AutoPipelineOptions controls batching: MaxBatchSize (soft target, default 200; the blocking face's preset uses 300), MaxBatchBytes (approximate payload cap so huge values flush as several bounded writes), MaxFlushDelay with optional AdaptiveDelay (delay scales down as the queue fills), and MaxConcurrentBatches (default 1 = a single ordered batch stream; raising it requires Unordered: true, so ordering is never lost by accident — Validate() rejects the combination otherwise). A usage tour and throughput comparison live in example/autopipeline.
Experimental: the API may change in a future release — pin your go-redis version if you adopt it.
(#3942) by @ndyakov, with help from @cxljs
This release adds support for Redis 8.10. The README's supported-versions list now includes Redis 8.10, and CI runs the full suite against the redislabs/client-libs-test:8.10.0 image by default (#3920, #3940).
Coverage for the new commands and options that ship with Redis 8.10:
HIMPORT(#3919) — bulk hash import via server-side fieldsets, exposed asHImportPrepare,HImportSet,HImportDiscard, andHImportDiscardAll. Fieldsets are session state scoped to a single physical connection, which does not mix well with connection pooling — so the client keeps a versioned fieldset registry and lazily replays thePREPAREon whichever pooled connection executes aSETthat needs it, at most once per connection, with no extra round trip (thePREPAREis injected into the same write as theSET).LMOVEM/BLMOVEM(#3913) — move multiple elements between lists in one call.SUNIONCARD/SDIFFCARD(#3897) — cardinality of set union/difference without materializing the result.XREAD/XREADGROUPMAXCOUNTandMAXSIZE(#3898) — bound how much data a stream read returns.TS.READ(#3896),TS.QUERYLABELS(#3926),TS.NRANGE/TS.NREVRANGE(#3870) with multiple aggregators per key (#3937), andEXCLUDEEMPTYonTS.MRANGE/TS.MREVRANGE(#3912) — new time-series query surface.FT.ALIASLIST(#3925),COLLECTreducer forFT.AGGREGATE(#3886),RERANKon HNSW vector fields inFT.CREATE(#3927), andFT.HYBRIDtimeout warnings (#3911) — search coverage.
Default configuration values now follow the cross-SDK configuration proposal shared by all Redis client libraries (#3918):
| Setting | Old default | New default |
|---|---|---|
ReadTimeout / WriteTimeout |
3s | 5s |
| Retry backoff (min/max) | 8ms / 512ms | 10ms / 1s |
| Cluster state reload interval | 10s | 60s |
| TCP keep-alive | 5min period | 30s idle / 5s interval / 3 probes (net.KeepAliveConfig) |
Applications that set these values explicitly are unaffected; applications relying on the old defaults inherit the new ones.
A systematic audit fixed data races across the client — hooks (AddHook, #3868), Ring.SetAddrs (#3862), cluster node slices (#3861), pub/sub reconnect (#3906), maintenance notifications (#3894, #3872), pool handoff (#3876), and redisotel (#3881) — and hardened the RESP parsers against malformed or unexpected replies: over-reads on nil replies (#3874), integer overflow when skipping map/attribute bodies (#3877), unhashable RESP3 map keys (#3873), odd-length flat replies (#3900), mismatched declared array lengths (#3907), unexpected extra reply frames (#3884), and nil elements in numeric/bool slice replies (#3922).
PeekPushNotificationName blocked until 36 bytes were buffered, so a short subscribe confirmation (channel name of six or fewer characters) on an otherwise idle connection hung PubSub.Receive forever — a regression introduced in 9.20.1 by #3842. The peek now parses whatever is already buffered and only waits for one more byte when the frame prefix is valid but incomplete. Fixes #3935.
The cluster transaction pipeline treated a MULTI...EXEC block as independently retryable commands, which could scatter a transaction across nodes or send malformed transactions on retry. Redirects (MOVED/ASK/TRYAGAIN) and aborts are now handled at the whole-transaction level, matching Redis transaction semantics: the transaction is re-routed and retried as a unit, never partially (#3909) by @cxljs.
rediscmd.AppendCmd — used by redisotel and rediscensus to render commands into span attributes — now redacts credential arguments as <redacted>: AUTH, HELLO ... AUTH, CONFIG SET of requirepass / masterauth / TLS key passphrases, ACL SETUSER password rules, and MIGRATE ... AUTH/AUTH2. The client sends HELLO ... AUTH on every handshake and AUTH on every streaming-credentials rotation through the regular hook chain, so tracing hooks previously captured credentials even when the application never issued an auth command itself (#3939) by @saddamr3e.
- Client-side caching: server-assisted caching for the standalone client via
ClientSideCacheConfig/ClientSideCache, with theCSCStrategySharedTrackinginvalidation strategy (#3941) by @ofekshenawa - Automatic pipelining:
AutoPipeline()(blocking) andAsyncAutoPipeline()(deferred results) onClientandClusterClient, configured viaAutoPipelineOptions(#3942) by @ndyakov, with help from @cxljs HIMPORTcommand family:HImportPrepare/HImportSet/HImportDiscard/HImportDiscardAllwith lazy per-connection fieldset prepare replay (#3919) by @ndyakovLMOVEM/BLMOVEM: move multiple list elements in one call, withCOUNT(up to N) orEXACTLY(all-or-nothing) semantics viaLMoveMArgs(#3913) by @ofekshenawaSUnionCard/SDiffCard: cardinality of set union/difference (#3897) by @ofekshenawaXRead/XReadGroupMAXCOUNT/MAXSIZE: bound stream read responses by entry count or payload size (#3898) by @ofekshenawaTS.READ: read samples from a series starting at a given timestamp, withTSReadEarliest(-),TSReadLatest(+), andTSReadNew($) sentinels (#3896) by @ofekshenawaTS.QUERYLABELS: query label names/values across time series (#3926) by @ndyakovTS.NRANGE/TS.NREVRANGE: range queries across multiple series (#3870) by @ofekshenawa, with multiple aggregators per key (#3937) by @ndyakovTS.MRANGE/TS.MREVRANGEEXCLUDEEMPTY: skip series with no samples in the result (#3912) by @ofekshenawaFT.ALIASLIST: list all index aliases (#3925) by @ndyakovFT.AGGREGATECOLLECTreducer: collect grouped values into an array (#3886) by @ndyakovFT.CREATERERANK:RERANKparameter on HNSW vector field definitions (#3927) by @ofekshenawaFT.HYBRIDtimeout warnings: timeout warnings are now populated in hybrid search results (#3911) by @ofekshenawaFT.HYBRIDKNNSHARD_K_RATIO(Redis 8.8+): per-shard K ratio for KNN clauses (#3841) by @ndyakov
- PubSub
Receivehang: peek push-notification names without demanding 36 buffered bytes, fixing a hang on short subscribe confirmations (fixes #3935, regression from 9.20.1) (#3936) by @ndyakov - Cluster transactions: re-route the whole tx pipeline on redirect/abort instead of per-command (#3909) by @cxljs
- Credential leak in traces:
rediscmd.AppendCmdredacts credential arguments (AUTH,HELLO ... AUTH,CONFIG SETsecret params,ACL SETUSERpassword rules,MIGRATE AUTH/AUTH2), soredisotel/rediscensusspan attributes no longer contain passwords (#3939) by @saddamr3e WaitAOFreturn type: returns*IntSliceCmdmatching the two-integerWAITAOFreply (#3888) by @CipherN9Ring.Publishrouting: publish to the shard that owns the topic instead of a round-robined one (#3893) by @dkindel- Pool
OnRemovehooks: fireOnRemoveonputConneviction paths so removal hooks see every evicted connection (#3932) by @cxljs UniversalClientInfoMap: addedInfoMapto theCmdableinterface (#3904) by @nazarli-shabnamSlowLogGetcontext: pass the caller's context instead of a background one (#3915) by @sonnemuskModuleLoadexnil config: return an error instead of panicking on nil config (#3916) by @sonnemuskParseURLIPv6 hosts: keep single brackets for IPv6 hosts without a port (#3882) by @sueun-devParseURLdurations: treat unit durations<= 0as disabled (#3866) by @sueun-dev- Nil
*uint8encoding: encode nil*uint8as"0"like other numeric pointers (#3869) by @sueun-dev JSONSliceCmdread errors: return the read error fromreadReplyinstead of swallowing it (#3903) by @saddamr3e- RESP parser hardening: reconcile declared entry-array lengths (#3907), handle nil elements in int/uint/bool slice parsers (#3922), drain unexpected reply frames (#3884), reject odd-length flat replies in Z/KeyValue parsers (#3900), avoid int overflow when skipping map/attr bodies (#3877), don't over-read nil replies in
Reader.Discard(#3874) by @saddamr3e; reject unhashable keys in RESP3 map parsing (#3873) by @iabdullah215 - Data races: hook state during
AddHook(#3868),onNewNodeduringRing.SetAddrs(#3862), shared masters/slaves slices in cluster (#3861), sharedopt.Addrduring pub/sub reconnect (#3906),clusterStateReloadCallbackin maintnotifications (#3894), conn reader inisHealthyConnduring handoff (#3876) by @saddamr3e; handoff race window in maintnotifications (#3872) by @ndyakov redisotel: useObservableCounterfor cumulative pool stats (#3914) by @Solaris-star; avoid a data race on shared attributes duringMinIdleConnswarmup (#3881) by @ndyakov
- Cross-SDK default alignment: new defaults for timeouts, retry backoff, cluster state reload, and TCP keep-alive (#3918) by @ndyakov
- CI on Redis 8.10: 8.10 made the default test version (#3920) with version gating by major.minor (#3908) by @ofekshenawa; the test stack now runs the GA
redislabs/client-libs-test:8.10.0image and 8.8 was dropped from the CI matrix (#3940) - Type-safe atomics: use typed
sync/atomicvalue types (#3860) and remove the deadassertUnstableCommandRESP3 path (#3928) by @cxljs - Docs: clarify that
ExpireTime/PExpireTimereturn Unix timestamps (#3917) by @sonnemusk; remove a duplicate example step (#3875) by @andy-stark-redis
We'd like to thank all the contributors who worked on this release!
@andy-stark-redis, @CipherN9, @cxljs, @dkindel, @iabdullah215, @nazarli-shabnam, @ndyakov, @ofekshenawa, @saddamr3e, @Solaris-star, @sonnemusk, @sueun-dev
Full Changelog: https://github.com/redis/go-redis/compare/v9.21.0...v9.22.0