diff --git a/.claude/skills/update-ci-image/SKILL.md b/.claude/skills/update-ci-image/SKILL.md index c9a47d63..19aecb0d 100644 --- a/.claude/skills/update-ci-image/SKILL.md +++ b/.claude/skills/update-ci-image/SKILL.md @@ -27,6 +27,7 @@ The `redislabs/client-libs-test` image is referenced in **six** places (five liv 3. `.github/workflows/build.yml` — update the `["8.X.x"]=""` line in `redis_version_mapping`. 4. `.github/actions/run-tests/action.yml` — update the matching `["8.X.x"]=""` line. 5. `.github/workflows/doctests.yaml` — update the `image:` line **only if** doctests should run against this version. Doctests typically pin to the latest stable; check before changing. +6. `CONTRIBUTING.md` — update the "By default the docker image … is `redislabs/client-libs-test:`." prose line to match the new `Makefile` default. This one is easy to forget because nothing breaks when it's stale. If the version that doctests run against (`Makefile` default) and the mapping for that same minor version diverge, you have a bug — they should resolve to the same tag. @@ -42,7 +43,7 @@ If the version that doctests run against (`Makefile` default) and the mapping fo 1. `.github/workflows/build.yml` — drop the `- ".x"` line from both `redis-version` matrices and the `[".x"]="..."` entry. 2. `.github/actions/run-tests/action.yml` — drop the same `[".x"]="..."` mapping entry. -3. If `Makefile` / `docker-compose.yml` / `doctests.yaml` were pinned to this version, pick a replacement (usually the next-newest version still in the matrix) and update them too. +3. If `Makefile` / `docker-compose.yml` / `doctests.yaml` were pinned to this version, pick a replacement (usually the next-newest version still in the matrix) and update them too, along with the `CONTRIBUTING.md` prose line. ### Switch to a custom build (e.g. `custom-26172898734-debian` for an unreleased server feature) @@ -60,7 +61,7 @@ After editing, search for any stale tag references: # Pattern: literal old tag, e.g. "8.8-rc1" or "client-libs-test:8.8" ``` -Expected matches: zero in `Makefile`, `docker-compose.yml`, `.github/**`. A reference in `AGENTS.md` or the `testing` skill documentation (e.g. "e.g. `redislabs/client-libs-test:8.8-m03`") is illustrative — leave it unless the doc is misleading after the change. +Expected matches: zero in `Makefile`, `docker-compose.yml`, `.github/**`, and `CONTRIBUTING.md`. A reference in `AGENTS.md` or the `testing` skill documentation (e.g. "e.g. `redislabs/client-libs-test:8.8-m03`") is illustrative — leave it unless the doc is misleading after the change. ## Gotchas diff --git a/.github/actions/run-tests/action.yml b/.github/actions/run-tests/action.yml index 265f9e78..8b19b743 100644 --- a/.github/actions/run-tests/action.yml +++ b/.github/actions/run-tests/action.yml @@ -47,8 +47,7 @@ runs: elif [[ -n "$REDIS_VERSION" ]]; then # Mapping of redis version to redis testing containers declare -A redis_version_mapping=( - ["8.10.x"]="custom-30445126297-debian" - ["8.8.x"]="8.8.0" + ["8.10.x"]="8.10.0" ["8.6.x"]="8.6.1" ["8.4.x"]="8.4.0" ["8.2.x"]="8.2.1-pre" diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d961379f..730442d6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -19,7 +19,6 @@ jobs: matrix: redis-version: - "8.10.x" # Redis CE 8.10 - - "8.8.x" # Redis CE 8.8 - "8.6.x" # Redis CE 8.6 - "8.4.x" # Redis CE 8.4 - "8.2.x" # Redis CE 8.2 @@ -48,8 +47,7 @@ jobs: # Mapping of redis version to redis testing containers declare -A redis_version_mapping=( - ["8.10.x"]="custom-30445126297-debian" - ["8.8.x"]="8.8.0" + ["8.10.x"]="8.10.0" ["8.6.x"]="8.6.1" ["8.4.x"]="8.4.0" ["8.2.x"]="8.2.1-pre" @@ -82,7 +80,6 @@ jobs: matrix: redis-version: - "8.10.x" # Redis CE 8.10 - - "8.8.x" # Redis CE 8.8 - "8.6.x" # Redis CE 8.6 - "8.4.x" # Redis CE 8.4 - "8.2.x" # Redis CE 8.2 diff --git a/.github/workflows/doctests.yaml b/.github/workflows/doctests.yaml index 32d7946a..980629f0 100644 --- a/.github/workflows/doctests.yaml +++ b/.github/workflows/doctests.yaml @@ -16,7 +16,7 @@ jobs: services: redis-stack: - image: redislabs/client-libs-test:custom-30445126297-debian + image: redislabs/client-libs-test:8.10.0 env: TLS_ENABLED: no REDIS_CLUSTER: no diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 04434136..9f175644 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -37,7 +37,7 @@ Here's how to get started with your code contribution: > Note: this clones and builds the docker containers specified in `docker-compose.yml`, to understand more about > the infrastructure that will be started you can check the `docker-compose.yml`. You also have the possiblity > to specify the redis image that will be pulled with the env variable `CLIENT_LIBS_TEST_IMAGE`. -> By default the docker image that will be pulled and started is `redislabs/client-libs-test:8.10-rc2`. +> By default the docker image that will be pulled and started is `redislabs/client-libs-test:8.10.0`. > If you want to test with newer Redis version, using a newer version of `redislabs/client-libs-test` should work out of the box. 4. While developing, make sure the tests pass by running `make test` (if you have the docker containers running, `make test.ci` may be sufficient). diff --git a/Makefile b/Makefile index 7d220269..a353f2ff 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ GO_MOD_DIRS := $(shell find . -type f -name 'go.mod' -exec dirname {} \; | sort) REDIS_VERSION ?= 8.10 RE_CLUSTER ?= false RCE_DOCKER ?= true -CLIENT_LIBS_TEST_IMAGE ?= redislabs/client-libs-test:custom-30445126297-debian +CLIENT_LIBS_TEST_IMAGE ?= redislabs/client-libs-test:8.10.0 docker.start: export RE_CLUSTER=$(RE_CLUSTER) && \ diff --git a/README.md b/README.md index 14286bdd..48f77f6f 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,7 @@ In `go-redis` we are aiming to support the last three releases of Redis. Current - [Redis 8.2](https://raw.githubusercontent.com/redis/redis/8.2/00-RELEASENOTES) - using Redis CE 8.2 - [Redis 8.4](https://raw.githubusercontent.com/redis/redis/8.4/00-RELEASENOTES) - using Redis CE 8.4 - [Redis 8.8](https://raw.githubusercontent.com/redis/redis/8.8/00-RELEASENOTES) - using Redis CE 8.8 +- [Redis 8.10](https://raw.githubusercontent.com/redis/redis/8.10/00-RELEASENOTES) - using Redis CE 8.10 Although the `go.mod` states it requires at minimum `go 1.24`, our CI is configured to run the tests against all supported versions of Redis and multiple versions of Go ([1.24](https://go.dev/doc/devel/release#go1.24.0), oldstable, and stable). We observe that some modules related test may not pass with diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index a125344d..40b0d1a4 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -1,5 +1,112 @@ # Release Notes +# 9.22.0-beta.1 (2026-07-29) + +This is a **beta** release adding support for Redis 8.10, new commands, and a large batch of stability and parser-robustness fixes. The 9.22.0 GA release will follow once client-side caching and auto-pipelining are merged. + +⚠️ Two changes to be aware of when upgrading from 9.21.0: + +- **Default configuration values changed** ([#3918](https://github.com/redis/go-redis/pull/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. +- **`WaitAOF` return type corrected** ([#3888](https://github.com/redis/go-redis/pull/3888)): `WaitAOF` now returns `*IntSliceCmd`, matching the two-integer reply of `WAITAOF` (previously `*IntCmd`, which failed to parse the reply at runtime). Code referencing the old return type needs a one-line update. + +## 🚀 Highlights + +### Redis 8.10 Support + +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](https://github.com/redis/go-redis/pull/3920), [#3940](https://github.com/redis/go-redis/pull/3940)). + +Coverage for the new commands and options that ship with Redis 8.10: + +- **`HIMPORT`** ([#3919](https://github.com/redis/go-redis/pull/3919)) — bulk hash import via server-side fieldsets, exposed as `HImportPrepare`, `HImportSet`, `HImportDiscard`, and `HImportDiscardAll`. 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 the `PREPARE` on whichever pooled connection executes a `SET` that needs it, at most once per connection, with no extra round trip (the `PREPARE` is injected into the same write as the `SET`). +- **`LMOVEM` / `BLMOVEM`** ([#3913](https://github.com/redis/go-redis/pull/3913)) — move multiple elements between lists in one call. +- **`SUNIONCARD` / `SDIFFCARD`** ([#3897](https://github.com/redis/go-redis/pull/3897)) — cardinality of set union/difference without materializing the result. +- **`XREAD` / `XREADGROUP` `MAXCOUNT` and `MAXSIZE`** ([#3898](https://github.com/redis/go-redis/pull/3898)) — bound how much data a stream read returns. +- **`TS.READ`** ([#3896](https://github.com/redis/go-redis/pull/3896)), **`TS.QUERYLABELS`** ([#3926](https://github.com/redis/go-redis/pull/3926)), **`TS.NRANGE` / `TS.NREVRANGE`** ([#3870](https://github.com/redis/go-redis/pull/3870)) with multiple aggregators per key ([#3937](https://github.com/redis/go-redis/pull/3937)), and **`EXCLUDEEMPTY`** on `TS.MRANGE` / `TS.MREVRANGE` ([#3912](https://github.com/redis/go-redis/pull/3912)) — new time-series query surface. +- **`FT.ALIASLIST`** ([#3925](https://github.com/redis/go-redis/pull/3925)), **`COLLECT` reducer for `FT.AGGREGATE`** ([#3886](https://github.com/redis/go-redis/pull/3886)), **`RERANK` on HNSW vector fields in `FT.CREATE`** ([#3927](https://github.com/redis/go-redis/pull/3927)), and **`FT.HYBRID` timeout warnings** ([#3911](https://github.com/redis/go-redis/pull/3911)) — search coverage. + +### Cross-SDK Aligned Defaults + +Default configuration values now follow the cross-SDK configuration proposal shared by all Redis client libraries ([#3918](https://github.com/redis/go-redis/pull/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. + +### Data-Race and Parser Hardening Sweep + +A systematic audit fixed data races across the client — hooks (`AddHook`, [#3868](https://github.com/redis/go-redis/pull/3868)), `Ring.SetAddrs` ([#3862](https://github.com/redis/go-redis/pull/3862)), cluster node slices ([#3861](https://github.com/redis/go-redis/pull/3861)), pub/sub reconnect ([#3906](https://github.com/redis/go-redis/pull/3906)), maintenance notifications ([#3894](https://github.com/redis/go-redis/pull/3894), [#3872](https://github.com/redis/go-redis/pull/3872)), pool handoff ([#3876](https://github.com/redis/go-redis/pull/3876)), and `redisotel` ([#3881](https://github.com/redis/go-redis/pull/3881)) — and hardened the RESP parsers against malformed or unexpected replies: over-reads on nil replies ([#3874](https://github.com/redis/go-redis/pull/3874)), integer overflow when skipping map/attribute bodies ([#3877](https://github.com/redis/go-redis/pull/3877)), unhashable RESP3 map keys ([#3873](https://github.com/redis/go-redis/pull/3873)), odd-length flat replies ([#3900](https://github.com/redis/go-redis/pull/3900)), mismatched declared array lengths ([#3907](https://github.com/redis/go-redis/pull/3907)), unexpected extra reply frames ([#3884](https://github.com/redis/go-redis/pull/3884)), and nil elements in numeric/bool slice replies ([#3922](https://github.com/redis/go-redis/pull/3922)). + +### PubSub `Receive` Hang Fix + +`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](https://github.com/redis/go-redis/pull/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](https://github.com/redis/go-redis/issues/3935). + +([#3936](https://github.com/redis/go-redis/pull/3936)) by [@ndyakov](https://github.com/ndyakov) + +### Correct Cluster Transaction Retries + +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](https://github.com/redis/go-redis/pull/3909)) by [@cxljs](https://github.com/cxljs). + +### Credential Redaction in Command Tracing + +`rediscmd.AppendCmd` — used by `redisotel` and `rediscensus` to render commands into span attributes — now redacts credential arguments as ``: `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](https://github.com/redis/go-redis/pull/3939)) by [@saddamr3e](https://github.com/saddamr3e). + +## ✨ New Features + +- **`HIMPORT` command family**: `HImportPrepare` / `HImportSet` / `HImportDiscard` / `HImportDiscardAll` with lazy per-connection fieldset prepare replay ([#3919](https://github.com/redis/go-redis/pull/3919)) by [@ndyakov](https://github.com/ndyakov) +- **`LMOVEM` / `BLMOVEM`**: move multiple list elements in one call, with `COUNT` (up to N) or `EXACTLY` (all-or-nothing) semantics via `LMoveMArgs` ([#3913](https://github.com/redis/go-redis/pull/3913)) by [@ofekshenawa](https://github.com/ofekshenawa) +- **`SUnionCard` / `SDiffCard`**: cardinality of set union/difference ([#3897](https://github.com/redis/go-redis/pull/3897)) by [@ofekshenawa](https://github.com/ofekshenawa) +- **`XRead` / `XReadGroup` `MAXCOUNT` / `MAXSIZE`**: bound stream read responses by entry count or payload size ([#3898](https://github.com/redis/go-redis/pull/3898)) by [@ofekshenawa](https://github.com/ofekshenawa) +- **`TS.READ`**: read samples from a series starting at a given timestamp, with `TSReadEarliest` (`-`), `TSReadLatest` (`+`), and `TSReadNew` (`$`) sentinels ([#3896](https://github.com/redis/go-redis/pull/3896)) by [@ofekshenawa](https://github.com/ofekshenawa) +- **`TS.QUERYLABELS`**: query label names/values across time series ([#3926](https://github.com/redis/go-redis/pull/3926)) by [@ndyakov](https://github.com/ndyakov) +- **`TS.NRANGE` / `TS.NREVRANGE`**: range queries across multiple series ([#3870](https://github.com/redis/go-redis/pull/3870)) by [@ofekshenawa](https://github.com/ofekshenawa), with multiple aggregators per key ([#3937](https://github.com/redis/go-redis/pull/3937)) by [@ndyakov](https://github.com/ndyakov) +- **`TS.MRANGE` / `TS.MREVRANGE` `EXCLUDEEMPTY`**: skip series with no samples in the result ([#3912](https://github.com/redis/go-redis/pull/3912)) by [@ofekshenawa](https://github.com/ofekshenawa) +- **`FT.ALIASLIST`**: list all index aliases ([#3925](https://github.com/redis/go-redis/pull/3925)) by [@ndyakov](https://github.com/ndyakov) +- **`FT.AGGREGATE` `COLLECT` reducer**: collect grouped values into an array ([#3886](https://github.com/redis/go-redis/pull/3886)) by [@ndyakov](https://github.com/ndyakov) +- **`FT.CREATE` `RERANK`**: `RERANK` parameter on HNSW vector field definitions ([#3927](https://github.com/redis/go-redis/pull/3927)) by [@ofekshenawa](https://github.com/ofekshenawa) +- **`FT.HYBRID` timeout warnings**: timeout warnings are now populated in hybrid search results ([#3911](https://github.com/redis/go-redis/pull/3911)) by [@ofekshenawa](https://github.com/ofekshenawa) +- **`FT.HYBRID` KNN `SHARD_K_RATIO`** (Redis 8.8+): per-shard K ratio for KNN clauses ([#3841](https://github.com/redis/go-redis/pull/3841)) by [@ndyakov](https://github.com/ndyakov) + +## 🐛 Bug Fixes + +- **PubSub `Receive` hang**: peek push-notification names without demanding 36 buffered bytes, fixing a hang on short subscribe confirmations (fixes [#3935](https://github.com/redis/go-redis/issues/3935), regression from 9.20.1) ([#3936](https://github.com/redis/go-redis/pull/3936)) by [@ndyakov](https://github.com/ndyakov) +- **Cluster transactions**: re-route the whole tx pipeline on redirect/abort instead of per-command ([#3909](https://github.com/redis/go-redis/pull/3909)) by [@cxljs](https://github.com/cxljs) +- **Credential leak in traces**: `rediscmd.AppendCmd` redacts credential arguments (`AUTH`, `HELLO ... AUTH`, `CONFIG SET` secret params, `ACL SETUSER` password rules, `MIGRATE AUTH`/`AUTH2`), so `redisotel` / `rediscensus` span attributes no longer contain passwords ([#3939](https://github.com/redis/go-redis/pull/3939)) by [@saddamr3e](https://github.com/saddamr3e) +- **`WaitAOF` return type**: returns `*IntSliceCmd` matching the two-integer `WAITAOF` reply ([#3888](https://github.com/redis/go-redis/pull/3888)) by [@CipherN9](https://github.com/CipherN9) +- **`Ring.Publish` routing**: publish to the shard that owns the topic instead of a round-robined one ([#3893](https://github.com/redis/go-redis/pull/3893)) by [@dkindel](https://github.com/dkindel) +- **Pool `OnRemove` hooks**: fire `OnRemove` on `putConn` eviction paths so removal hooks see every evicted connection ([#3932](https://github.com/redis/go-redis/pull/3932)) by [@cxljs](https://github.com/cxljs) +- **`UniversalClient` `InfoMap`**: added `InfoMap` to the `Cmdable` interface ([#3904](https://github.com/redis/go-redis/pull/3904)) by [@nazarli-shabnam](https://github.com/nazarli-shabnam) +- **`SlowLogGet` context**: pass the caller's context instead of a background one ([#3915](https://github.com/redis/go-redis/pull/3915)) by [@sonnemusk](https://github.com/sonnemusk) +- **`ModuleLoadex` nil config**: return an error instead of panicking on nil config ([#3916](https://github.com/redis/go-redis/pull/3916)) by [@sonnemusk](https://github.com/sonnemusk) +- **`ParseURL` IPv6 hosts**: keep single brackets for IPv6 hosts without a port ([#3882](https://github.com/redis/go-redis/pull/3882)) by [@sueun-dev](https://github.com/sueun-dev) +- **`ParseURL` durations**: treat unit durations `<= 0` as disabled ([#3866](https://github.com/redis/go-redis/pull/3866)) by [@sueun-dev](https://github.com/sueun-dev) +- **Nil `*uint8` encoding**: encode nil `*uint8` as `"0"` like other numeric pointers ([#3869](https://github.com/redis/go-redis/pull/3869)) by [@sueun-dev](https://github.com/sueun-dev) +- **`JSONSliceCmd` read errors**: return the read error from `readReply` instead of swallowing it ([#3903](https://github.com/redis/go-redis/pull/3903)) by [@saddamr3e](https://github.com/saddamr3e) +- **RESP parser hardening**: reconcile declared entry-array lengths ([#3907](https://github.com/redis/go-redis/pull/3907)), handle nil elements in int/uint/bool slice parsers ([#3922](https://github.com/redis/go-redis/pull/3922)), drain unexpected reply frames ([#3884](https://github.com/redis/go-redis/pull/3884)), reject odd-length flat replies in Z/KeyValue parsers ([#3900](https://github.com/redis/go-redis/pull/3900)), avoid int overflow when skipping map/attr bodies ([#3877](https://github.com/redis/go-redis/pull/3877)), don't over-read nil replies in `Reader.Discard` ([#3874](https://github.com/redis/go-redis/pull/3874)) by [@saddamr3e](https://github.com/saddamr3e); reject unhashable keys in RESP3 map parsing ([#3873](https://github.com/redis/go-redis/pull/3873)) by [@iabdullah215](https://github.com/iabdullah215) +- **Data races**: hook state during `AddHook` ([#3868](https://github.com/redis/go-redis/pull/3868)), `onNewNode` during `Ring.SetAddrs` ([#3862](https://github.com/redis/go-redis/pull/3862)), shared masters/slaves slices in cluster ([#3861](https://github.com/redis/go-redis/pull/3861)), shared `opt.Addr` during pub/sub reconnect ([#3906](https://github.com/redis/go-redis/pull/3906)), `clusterStateReloadCallback` in maintnotifications ([#3894](https://github.com/redis/go-redis/pull/3894)), conn reader in `isHealthyConn` during handoff ([#3876](https://github.com/redis/go-redis/pull/3876)) by [@saddamr3e](https://github.com/saddamr3e); handoff race window in maintnotifications ([#3872](https://github.com/redis/go-redis/pull/3872)) by [@ndyakov](https://github.com/ndyakov) +- **`redisotel`**: use `ObservableCounter` for cumulative pool stats ([#3914](https://github.com/redis/go-redis/pull/3914)) by [@Solaris-star](https://github.com/Solaris-star); avoid a data race on shared attributes during `MinIdleConns` warmup ([#3881](https://github.com/redis/go-redis/pull/3881)) by [@ndyakov](https://github.com/ndyakov) + +## 🧰 Maintenance + +- **Cross-SDK default alignment**: new defaults for timeouts, retry backoff, cluster state reload, and TCP keep-alive ([#3918](https://github.com/redis/go-redis/pull/3918)) by [@ndyakov](https://github.com/ndyakov) +- **CI on Redis 8.10**: 8.10 made the default test version ([#3920](https://github.com/redis/go-redis/pull/3920)) with version gating by major.minor ([#3908](https://github.com/redis/go-redis/pull/3908)) by [@ofekshenawa](https://github.com/ofekshenawa); the test stack now runs the GA `redislabs/client-libs-test:8.10.0` image and 8.8 was dropped from the CI matrix ([#3940](https://github.com/redis/go-redis/pull/3940)) +- **Type-safe atomics**: use typed `sync/atomic` value types ([#3860](https://github.com/redis/go-redis/pull/3860)) and remove the dead `assertUnstableCommand` RESP3 path ([#3928](https://github.com/redis/go-redis/pull/3928)) by [@cxljs](https://github.com/cxljs) +- **Docs**: clarify that `ExpireTime` / `PExpireTime` return Unix timestamps ([#3917](https://github.com/redis/go-redis/pull/3917)) by [@sonnemusk](https://github.com/sonnemusk); remove a duplicate example step ([#3875](https://github.com/redis/go-redis/pull/3875)) by [@andy-stark-redis](https://github.com/andy-stark-redis) + +## 👥 Contributors + +We'd like to thank all the contributors who worked on this release! + +[@andy-stark-redis](https://github.com/andy-stark-redis), [@CipherN9](https://github.com/CipherN9), [@cxljs](https://github.com/cxljs), [@dkindel](https://github.com/dkindel), [@iabdullah215](https://github.com/iabdullah215), [@nazarli-shabnam](https://github.com/nazarli-shabnam), [@ndyakov](https://github.com/ndyakov), [@ofekshenawa](https://github.com/ofekshenawa), [@saddamr3e](https://github.com/saddamr3e), [@Solaris-star](https://github.com/Solaris-star), [@sonnemusk](https://github.com/sonnemusk), [@sueun-dev](https://github.com/sueun-dev) + +--- + +**Full Changelog**: https://github.com/redis/go-redis/compare/v9.21.0...v9.22.0-beta.1 + # 9.21.0 (2026-06-18) This is a minor release adding new features and bug fixes. There are no breaking changes; upgrading from 9.20.x is a drop-in replacement. diff --git a/docker-compose.yml b/docker-compose.yml index 9205c0e9..de9683a6 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,6 +1,6 @@ --- -x-default-image: &default-image ${CLIENT_LIBS_TEST_IMAGE:-redislabs/client-libs-test:custom-30445126297-debian} +x-default-image: &default-image ${CLIENT_LIBS_TEST_IMAGE:-redislabs/client-libs-test:8.10.0} services: redis: diff --git a/example/cluster-mget/go.mod b/example/cluster-mget/go.mod index 9c9e5766..b1506d89 100644 --- a/example/cluster-mget/go.mod +++ b/example/cluster-mget/go.mod @@ -4,7 +4,7 @@ go 1.24 replace github.com/redis/go-redis/v9 => ../.. -require github.com/redis/go-redis/v9 v9.21.0 +require github.com/redis/go-redis/v9 v9.22.0-beta.1 require ( github.com/cespare/xxhash/v2 v2.3.0 // indirect diff --git a/example/del-keys-without-ttl/go.mod b/example/del-keys-without-ttl/go.mod index f55e1758..df6791df 100644 --- a/example/del-keys-without-ttl/go.mod +++ b/example/del-keys-without-ttl/go.mod @@ -5,7 +5,7 @@ go 1.24 replace github.com/redis/go-redis/v9 => ../.. require ( - github.com/redis/go-redis/v9 v9.21.0 + github.com/redis/go-redis/v9 v9.22.0-beta.1 go.uber.org/zap v1.24.0 ) diff --git a/example/digest-optimistic-locking/go.mod b/example/digest-optimistic-locking/go.mod index 1f16059e..edc5e985 100644 --- a/example/digest-optimistic-locking/go.mod +++ b/example/digest-optimistic-locking/go.mod @@ -4,7 +4,7 @@ go 1.24 replace github.com/redis/go-redis/v9 => ../.. -require github.com/redis/go-redis/v9 v9.21.0 +require github.com/redis/go-redis/v9 v9.22.0-beta.1 require ( github.com/cespare/xxhash/v2 v2.3.0 // indirect diff --git a/example/disable-maintnotifications/go.mod b/example/disable-maintnotifications/go.mod index 290de1c1..53b5a6e6 100644 --- a/example/disable-maintnotifications/go.mod +++ b/example/disable-maintnotifications/go.mod @@ -4,7 +4,7 @@ go 1.24 replace github.com/redis/go-redis/v9 => ../.. -require github.com/redis/go-redis/v9 v9.21.0 +require github.com/redis/go-redis/v9 v9.22.0-beta.1 require ( github.com/cespare/xxhash/v2 v2.3.0 // indirect diff --git a/example/ft-aliaslist/go.mod b/example/ft-aliaslist/go.mod index a1eeab9c..3ab92ea7 100644 --- a/example/ft-aliaslist/go.mod +++ b/example/ft-aliaslist/go.mod @@ -4,7 +4,7 @@ go 1.24 replace github.com/redis/go-redis/v9 => ../.. -require github.com/redis/go-redis/v9 v9.21.0 +require github.com/redis/go-redis/v9 v9.22.0-beta.1 require ( github.com/cespare/xxhash/v2 v2.3.0 // indirect diff --git a/example/himport/go.mod b/example/himport/go.mod index 9eb7132b..806abe25 100644 --- a/example/himport/go.mod +++ b/example/himport/go.mod @@ -4,7 +4,7 @@ go 1.24 replace github.com/redis/go-redis/v9 => ../.. -require github.com/redis/go-redis/v9 v9.21.0 +require github.com/redis/go-redis/v9 v9.22.0-beta.1 require ( github.com/cespare/xxhash/v2 v2.3.0 // indirect diff --git a/example/hll/go.mod b/example/hll/go.mod index 4aa6e8bc..67d9ca18 100644 --- a/example/hll/go.mod +++ b/example/hll/go.mod @@ -4,7 +4,7 @@ go 1.24 replace github.com/redis/go-redis/v9 => ../.. -require github.com/redis/go-redis/v9 v9.21.0 +require github.com/redis/go-redis/v9 v9.22.0-beta.1 require ( github.com/cespare/xxhash/v2 v2.3.0 // indirect diff --git a/example/hset-struct/go.mod b/example/hset-struct/go.mod index 1596e4df..a47e4946 100644 --- a/example/hset-struct/go.mod +++ b/example/hset-struct/go.mod @@ -6,7 +6,7 @@ replace github.com/redis/go-redis/v9 => ../.. require ( github.com/davecgh/go-spew v1.1.1 - github.com/redis/go-redis/v9 v9.21.0 + github.com/redis/go-redis/v9 v9.22.0-beta.1 ) require ( diff --git a/example/lua-scripting/go.mod b/example/lua-scripting/go.mod index d91d97d3..0320b0c1 100644 --- a/example/lua-scripting/go.mod +++ b/example/lua-scripting/go.mod @@ -4,7 +4,7 @@ go 1.24 replace github.com/redis/go-redis/v9 => ../.. -require github.com/redis/go-redis/v9 v9.21.0 +require github.com/redis/go-redis/v9 v9.22.0-beta.1 require ( github.com/cespare/xxhash/v2 v2.3.0 // indirect diff --git a/example/maintnotifiations-pubsub/go.mod b/example/maintnotifiations-pubsub/go.mod index 5764cd55..46072ee1 100644 --- a/example/maintnotifiations-pubsub/go.mod +++ b/example/maintnotifiations-pubsub/go.mod @@ -4,7 +4,7 @@ go 1.24 replace github.com/redis/go-redis/v9 => ../.. -require github.com/redis/go-redis/v9 v9.21.0 +require github.com/redis/go-redis/v9 v9.22.0-beta.1 require ( github.com/cespare/xxhash/v2 v2.3.0 // indirect diff --git a/example/otel-metrics/go.mod b/example/otel-metrics/go.mod index 9285c5c4..44e4b88e 100644 --- a/example/otel-metrics/go.mod +++ b/example/otel-metrics/go.mod @@ -7,8 +7,8 @@ replace github.com/redis/go-redis/v9 => ../.. replace github.com/redis/go-redis/extra/redisotel-native/v9 => ../../extra/redisotel-native require ( - github.com/redis/go-redis/extra/redisotel-native/v9 v9.21.0 - github.com/redis/go-redis/v9 v9.21.0 + github.com/redis/go-redis/extra/redisotel-native/v9 v9.22.0-beta.1 + github.com/redis/go-redis/v9 v9.22.0-beta.1 go.opentelemetry.io/otel v1.43.0 go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.43.0 go.opentelemetry.io/otel/sdk/metric v1.43.0 diff --git a/example/otel/go.mod b/example/otel/go.mod index 98b88dae..955eecd1 100644 --- a/example/otel/go.mod +++ b/example/otel/go.mod @@ -9,8 +9,8 @@ replace github.com/redis/go-redis/extra/redisotel/v9 => ../../extra/redisotel replace github.com/redis/go-redis/extra/rediscmd/v9 => ../../extra/rediscmd require ( - github.com/redis/go-redis/extra/redisotel/v9 v9.21.0 - github.com/redis/go-redis/v9 v9.21.0 + github.com/redis/go-redis/extra/redisotel/v9 v9.22.0-beta.1 + github.com/redis/go-redis/v9 v9.22.0-beta.1 github.com/uptrace/uptrace-go v1.41.1 go.opentelemetry.io/otel v1.43.0 ) @@ -22,7 +22,7 @@ require ( github.com/go-logr/stdr v1.2.2 // indirect github.com/google/uuid v1.6.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 // indirect - github.com/redis/go-redis/extra/rediscmd/v9 v9.21.0 // indirect + github.com/redis/go-redis/extra/rediscmd/v9 v9.22.0-beta.1 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect go.opentelemetry.io/contrib/instrumentation/runtime v0.68.0 // indirect go.opentelemetry.io/contrib/processors/minsev v0.16.0 // indirect diff --git a/example/redis-bloom/go.mod b/example/redis-bloom/go.mod index a4e6d91f..de7f8838 100644 --- a/example/redis-bloom/go.mod +++ b/example/redis-bloom/go.mod @@ -4,7 +4,7 @@ go 1.24 replace github.com/redis/go-redis/v9 => ../.. -require github.com/redis/go-redis/v9 v9.21.0 +require github.com/redis/go-redis/v9 v9.22.0-beta.1 require ( github.com/cespare/xxhash/v2 v2.3.0 // indirect diff --git a/example/scan-struct/go.mod b/example/scan-struct/go.mod index 1596e4df..a47e4946 100644 --- a/example/scan-struct/go.mod +++ b/example/scan-struct/go.mod @@ -6,7 +6,7 @@ replace github.com/redis/go-redis/v9 => ../.. require ( github.com/davecgh/go-spew v1.1.1 - github.com/redis/go-redis/v9 v9.21.0 + github.com/redis/go-redis/v9 v9.22.0-beta.1 ) require ( diff --git a/example/search-aggregate-collect/go.mod b/example/search-aggregate-collect/go.mod index f520c741..ad54b570 100644 --- a/example/search-aggregate-collect/go.mod +++ b/example/search-aggregate-collect/go.mod @@ -4,7 +4,7 @@ go 1.24 replace github.com/redis/go-redis/v9 => ../.. -require github.com/redis/go-redis/v9 v9.21.0 +require github.com/redis/go-redis/v9 v9.22.0-beta.1 require ( github.com/cespare/xxhash/v2 v2.3.0 // indirect diff --git a/example/search-aggregate-steps/go.mod b/example/search-aggregate-steps/go.mod index e3d50f5a..5304e937 100644 --- a/example/search-aggregate-steps/go.mod +++ b/example/search-aggregate-steps/go.mod @@ -4,7 +4,7 @@ go 1.24 replace github.com/redis/go-redis/v9 => ../.. -require github.com/redis/go-redis/v9 v9.21.0 +require github.com/redis/go-redis/v9 v9.22.0-beta.1 require ( github.com/cespare/xxhash/v2 v2.3.0 // indirect diff --git a/example/tls-cert-auth/go.mod b/example/tls-cert-auth/go.mod index e32aeab7..cdee7ae2 100644 --- a/example/tls-cert-auth/go.mod +++ b/example/tls-cert-auth/go.mod @@ -4,7 +4,7 @@ go 1.24 replace github.com/redis/go-redis/v9 => ../.. -require github.com/redis/go-redis/v9 v9.21.0 +require github.com/redis/go-redis/v9 v9.22.0-beta.1 require ( github.com/cespare/xxhash/v2 v2.3.0 // indirect diff --git a/example/tls-connection/go.mod b/example/tls-connection/go.mod index ae5f9f05..10612ad0 100644 --- a/example/tls-connection/go.mod +++ b/example/tls-connection/go.mod @@ -4,7 +4,7 @@ go 1.24 replace github.com/redis/go-redis/v9 => ../.. -require github.com/redis/go-redis/v9 v9.21.0 +require github.com/redis/go-redis/v9 v9.22.0-beta.1 require ( github.com/cespare/xxhash/v2 v2.3.0 // indirect diff --git a/example/ts-querylabels/go.mod b/example/ts-querylabels/go.mod index 16261dbb..f11e5a75 100644 --- a/example/ts-querylabels/go.mod +++ b/example/ts-querylabels/go.mod @@ -4,7 +4,7 @@ go 1.24 replace github.com/redis/go-redis/v9 => ../.. -require github.com/redis/go-redis/v9 v9.21.0 +require github.com/redis/go-redis/v9 v9.22.0-beta.1 require ( github.com/cespare/xxhash/v2 v2.3.0 // indirect diff --git a/example/zerocopy-buffer/go.mod b/example/zerocopy-buffer/go.mod index 185fd45b..5d6b353a 100644 --- a/example/zerocopy-buffer/go.mod +++ b/example/zerocopy-buffer/go.mod @@ -4,7 +4,7 @@ go 1.24 replace github.com/redis/go-redis/v9 => ../.. -require github.com/redis/go-redis/v9 v9.20.0 +require github.com/redis/go-redis/v9 v9.22.0-beta.1 require ( github.com/cespare/xxhash/v2 v2.3.0 // indirect diff --git a/extra/rediscensus/go.mod b/extra/rediscensus/go.mod index 7cc7685b..bc5e49c1 100644 --- a/extra/rediscensus/go.mod +++ b/extra/rediscensus/go.mod @@ -7,8 +7,8 @@ replace github.com/redis/go-redis/v9 => ../.. replace github.com/redis/go-redis/extra/rediscmd/v9 => ../rediscmd require ( - github.com/redis/go-redis/extra/rediscmd/v9 v9.21.0 - github.com/redis/go-redis/v9 v9.21.0 + github.com/redis/go-redis/extra/rediscmd/v9 v9.22.0-beta.1 + github.com/redis/go-redis/v9 v9.22.0-beta.1 go.opencensus.io v0.24.0 ) diff --git a/extra/rediscmd/go.mod b/extra/rediscmd/go.mod index c35c67e8..ea7b5860 100644 --- a/extra/rediscmd/go.mod +++ b/extra/rediscmd/go.mod @@ -7,7 +7,7 @@ replace github.com/redis/go-redis/v9 => ../.. require ( github.com/bsm/ginkgo/v2 v2.12.0 github.com/bsm/gomega v1.27.10 - github.com/redis/go-redis/v9 v9.21.0 + github.com/redis/go-redis/v9 v9.22.0-beta.1 ) require ( diff --git a/extra/redisotel-native/go.mod b/extra/redisotel-native/go.mod index e3adf989..98e7e50e 100644 --- a/extra/redisotel-native/go.mod +++ b/extra/redisotel-native/go.mod @@ -5,7 +5,7 @@ go 1.25.0 replace github.com/redis/go-redis/v9 => ../.. require ( - github.com/redis/go-redis/v9 v9.21.0 + github.com/redis/go-redis/v9 v9.22.0-beta.1 go.opentelemetry.io/otel v1.43.0 go.opentelemetry.io/otel/metric v1.43.0 go.opentelemetry.io/otel/sdk/metric v1.43.0 diff --git a/extra/redisotel/go.mod b/extra/redisotel/go.mod index 76e991b3..92d49e57 100644 --- a/extra/redisotel/go.mod +++ b/extra/redisotel/go.mod @@ -7,8 +7,8 @@ replace github.com/redis/go-redis/v9 => ../.. replace github.com/redis/go-redis/extra/rediscmd/v9 => ../rediscmd require ( - github.com/redis/go-redis/extra/rediscmd/v9 v9.21.0 - github.com/redis/go-redis/v9 v9.21.0 + github.com/redis/go-redis/extra/rediscmd/v9 v9.22.0-beta.1 + github.com/redis/go-redis/v9 v9.22.0-beta.1 go.opentelemetry.io/otel v1.43.0 go.opentelemetry.io/otel/metric v1.43.0 go.opentelemetry.io/otel/sdk v1.43.0 diff --git a/extra/redisprometheus/go.mod b/extra/redisprometheus/go.mod index 33b96d5b..be36db3a 100644 --- a/extra/redisprometheus/go.mod +++ b/extra/redisprometheus/go.mod @@ -6,7 +6,7 @@ replace github.com/redis/go-redis/v9 => ../.. require ( github.com/prometheus/client_golang v1.14.0 - github.com/redis/go-redis/v9 v9.21.0 + github.com/redis/go-redis/v9 v9.22.0-beta.1 ) require ( diff --git a/version.go b/version.go index c6cabc69..e4caf6e9 100644 --- a/version.go +++ b/version.go @@ -2,5 +2,5 @@ package redis // Version is the current release version. func Version() string { - return "9.21.0" + return "9.22.0-beta.1" }