From 344e9562b3a5a2d73c2a4040f4fafe7263672078 Mon Sep 17 00:00:00 2001 From: David Dougherty Date: Fri, 3 Oct 2025 06:29:03 -0700 Subject: [PATCH 01/17] DOC-5799: search: document procedure for building with Intel binaries (#2206) * DOC-5799: search: document procedure for building with Intel binaries * Fix warning shortcode use --- .../vectors/svs-compression.md | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/content/develop/ai/search-and-query/vectors/svs-compression.md b/content/develop/ai/search-and-query/vectors/svs-compression.md index aa2c581447..735fc435c6 100644 --- a/content/develop/ai/search-and-query/vectors/svs-compression.md +++ b/content/develop/ai/search-and-query/vectors/svs-compression.md @@ -102,3 +102,27 @@ The strong performance of LVQ and LeanVec stems from their ability to adapt to t A minimum number of representative vectors is required during index initialization to train the compression parameters (see the [TRAINING_THRESHOLD]({{< relref "/develop/ai/search-and-query/vectors/#svs-vamana-index" >}}) parameter). A random sample from the dataset typically works well. * **Handling data drift:** If the characteristics of incoming vectors change significantly over time (that is, a data distribution shift), compression quality may degrade. This is a general limitation of all data-dependent compression methods,not just LVQ and LeanVec. When the data no longer resembles the original training sample, the learned representation becomes less effective. + +## Build Redis Open Source with Intel SVS support + +By default, Redis Open Source with the Redis Query Engine supports SVS-VAMANA indexing with the global 8-bit quantisation. To compile Redis with the Intel SVS-VAMANA optimisations, LeanVec and LVQ, for Intel platforms, follow the instructions below. + +{{< warning >}} +If you are using Redis Open Source under the AGPLv3 or SSPLv1 licenses, you cannot use it together with the Intel Optimization binaries (LeanVec and LVQ). The reason is that the Intel SVS license is not compatible with those licenses. +The LeanVec and LVQ techniques are closed source and are only available for use with Redis Open Source when distributed under the RSALv2 license. +For more details, please refer to the [information provided by Intel](https://github.com/intel/ScalableVectorSearch). +{{< /warning >}} + +### Build Redis Open Source + +Follow the [Redis Open Source build instructions]({{< relref "/operate/oss_and_stack/install/build-stack" >}}). Before executing `make`, define the following environment variable. + +```sh +export BUILD_INTEL_SVS_OPT=yes +``` + +Alternatively, you can define the `BUILD_INTEL_SVS_OPT` variable as part of the `make` command: + +```sh +make BUILD_INTEL_SVS_OPT=yes +``` From cab5aa762bcf77f282531425031abe1c9bab20bb Mon Sep 17 00:00:00 2001 From: David Dougherty Date: Fri, 3 Oct 2025 07:53:24 -0700 Subject: [PATCH 02/17] DOC-5777: search: document new SCORERs (#2204) * DOC-5777: search: document new SCORERs * Update the admin. overview page * Apply suggestions from code review Co-authored-by: andy-stark-redis <164213578+andy-stark-redis@users.noreply.github.com> * Apply suggestions from code review --------- Co-authored-by: andy-stark-redis <164213578+andy-stark-redis@users.noreply.github.com> --- .../administration/overview.md | 18 ++++++++++-- .../advanced-concepts/scoring.md | 28 +++++++++++++++++-- 2 files changed, 41 insertions(+), 5 deletions(-) diff --git a/content/develop/ai/search-and-query/administration/overview.md b/content/develop/ai/search-and-query/administration/overview.md index d59e5ad0d6..c9ace1efb2 100644 --- a/content/develop/ai/search-and-query/administration/overview.md +++ b/content/develop/ai/search-and-query/administration/overview.md @@ -239,9 +239,23 @@ These are the pre-bundled scoring functions available in Redis: * Identical to the default TFIDF scorer, with one important distinction: -* **BM25** +* **BM25STD (default)** - A variation on the basic TF-IDF scorer. See [this Wikipedia article for more information](https://en.wikipedia.org/wiki/Okapi_BM25). + A variation on the basic `TFIDF` scorer, see [this Wikipedia article for more info](https://en.wikipedia.org/wiki/Okapi_BM25). + + The relevance score for each document is multiplied by the presumptive document score and a penalty is applied based on slop as in `TFIDF`. + + {{< note >}} + The `BM25` scorer was renamed `BM25STD` in Redis Open Source 8.4. `BM25` is deprecated. + {{< /note >}} + +* **BM25STD.NORM** + + A variation of `BM25STD`, where the scores are normalized by the minimum and maximum score. + +* **BM25STD.TANH** + + A variation of `BM25STD.NORM`, where the scores are normalised by linear function `tanh(x)`. `BMSTDSTD.TANH` can take an optional argument, `BM25STD_TANH_FACTOR Y`, which is used to smooth the function and the score values. The default value for `Y` is 4. * **DISMAX** diff --git a/content/develop/ai/search-and-query/advanced-concepts/scoring.md b/content/develop/ai/search-and-query/advanced-concepts/scoring.md index c09bb64e80..0a8e2e410d 100644 --- a/content/develop/ai/search-and-query/advanced-concepts/scoring.md +++ b/content/develop/ai/search-and-query/advanced-concepts/scoring.md @@ -19,7 +19,7 @@ weight: 8 When searching, documents are scored based on their relevance to the query. The score is a floating point number between 0.0 and 1.0, where 1.0 is the highest score. The score is returned as part of the search results and can be used to sort the results. -Redis Open Source comes with a few very basic scoring functions to evaluate document relevance. They are all based on document scores and term frequency. This is regardless of the ability to use [sortable fields]({{< relref "/develop/ai/search-and-query/advanced-concepts/sorting" >}}). Scoring functions are specified by adding the `SCORER {scorer_name}` argument to a search query. +Redis Open Source comes with a few scoring functions to evaluate document relevance. They are all based on document scores and term frequency. This is regardless of the ability to use [sortable fields]({{< relref "/develop/ai/search-and-query/advanced-concepts/sorting" >}}). Scoring functions are specified by adding the `SCORER {scorer_name}` argument to a search query. If you prefer a custom scoring function, it is possible to add more functions using the [extension API]({{< relref "/develop/ai/search-and-query/administration/extensions" >}}). @@ -78,14 +78,36 @@ Term frequencies are normalized by the length of the document, expressed as the FT.SEARCH myIndex "foo" SCORER TFIDF.DOCNORM ``` -## BM25 (default) +## BM25STD (default) A variation on the basic `TFIDF` scorer, see [this Wikipedia article for more info](https://en.wikipedia.org/wiki/Okapi_BM25). The relevance score for each document is multiplied by the presumptive document score and a penalty is applied based on slop as in `TFIDF`. +{{< note >}} +The `BM25` scorer was renamed `BM25STD` in Redis Open Source 8.4. `BM25` is deprecated. +{{< /note >}} + +``` +FT.SEARCH myIndex "foo" SCORER BM25STD +``` + +## BM25STD.NORM + +A variation of `BM25STD`, where the scores are normalized by the minimum and maximum scores. + +`BM25STD.NORM` uses min–max normalization across the collection, making it more accurate in distinguishing documents when term frequency distributions vary significantly. Because it depends on global statistics, results adapt better to collection-specific characteristics, but this comes at a performance cost: min and max values must be computed and updated whenever the collection changes. This method is recommended when ranking precision is critical and the dataset is relatively stable. + +## BM25STD.TANH + +A variation of `BM25STD.NORM`, where the scores are normalised by linear function `tanh(x)`. `BMSTDSTD.TANH` can take an optional argument, `BM25STD_TANH_FACTOR Y`, which is used to smooth the function and the score values. The default value for `Y` is 4. + +`BM25STD.TANH` applies a smooth transformation using the `tanh(x/factor)` function, which avoids collection-dependent statistics and yields faster, more efficient scoring. While this makes it more scalable and consistent across different datasets, the trade-off is reduced accuracy in cases where min–max normalization provides sharper separation. This method is recommended when performance and throughput are prioritized over fine-grained ranking sensitivity. + +Following is an example of how to use `BM25STD_TANH_FACTOR Y` in a query. + ``` -FT.SEARCH myIndex "foo" SCORER BM25 +FT.SEARCH idx "term" SCORER BM25STD.TANH BM25STD_TANH_FACTOR 12 WITHSCORES ``` ## DISMAX From 576726bae0b0cbaf4798164725e777eb0d04a5d5 Mon Sep 17 00:00:00 2001 From: David Dougherty Date: Tue, 14 Oct 2025 11:21:14 -0700 Subject: [PATCH 03/17] DOC-5818: vector set: add new VRANGE command (#2233) --- content/commands/vadd.md | 1 - content/commands/vcard.md | 1 - content/commands/vdim.md | 1 - content/commands/vemb.md | 1 - content/commands/vgetattr.md | 1 - content/commands/vinfo.md | 1 - content/commands/vismember.md | 1 - content/commands/vlinks.md | 1 - content/commands/vrandmember.md | 1 - content/commands/vrange.md | 126 ++++++++++++++++++ content/commands/vrem.md | 1 - content/commands/vsetattr.md | 1 - content/commands/vsim.md | 1 - .../develop/data-types/vector-sets/_index.md | 2 - 14 files changed, 126 insertions(+), 14 deletions(-) create mode 100644 content/commands/vrange.md diff --git a/content/commands/vadd.md b/content/commands/vadd.md index 735077229d..a2418d0a48 100644 --- a/content/commands/vadd.md +++ b/content/commands/vadd.md @@ -18,7 +18,6 @@ since: 8.0.0 summary: Add a new element to a vector set, or update its vector if it already exists. syntax_fmt: "VADD key [REDUCE dim] (FP32 | VALUES num) vector element [CAS] [NOQUANT | Q8 | BIN]\n [EF build-exploration-factor] [SETATTR attributes] [M numlinks]" title: VADD -bannerText: Vector set is a new data type that is currently in preview and may be subject to change. --- Add a new element into the vector set specified by `key`. The vector can be provided as 32-bit floating point (`FP32`) blob of values, or as floating point numbers as strings, prefixed by the number of elements (3 in the example below): diff --git a/content/commands/vcard.md b/content/commands/vcard.md index c0c1159085..ab77d1f7e3 100644 --- a/content/commands/vcard.md +++ b/content/commands/vcard.md @@ -18,7 +18,6 @@ since: 8.0.0 summary: Return the number of elements in a vector set. syntax_fmt: "VCARD key" title: VCARD -bannerText: Vector set is a new data type that is currently in preview and may be subject to change. --- Return the number of elements in the specified vector set. diff --git a/content/commands/vdim.md b/content/commands/vdim.md index c133f1d5a6..1a889812dc 100644 --- a/content/commands/vdim.md +++ b/content/commands/vdim.md @@ -18,7 +18,6 @@ since: 8.0.0 summary: Return the dimension of vectors in the vector set. syntax_fmt: "VDIM key" title: VDIM -bannerText: Vector set is a new data type that is currently in preview and may be subject to change. --- Return the number of dimensions of the vectors in the specified vector set. diff --git a/content/commands/vemb.md b/content/commands/vemb.md index 40eefa0249..5f2f0ab8a8 100644 --- a/content/commands/vemb.md +++ b/content/commands/vemb.md @@ -18,7 +18,6 @@ since: 8.0.0 summary: Return the vector associated with an element. syntax_fmt: "VEMB key element [RAW]" title: VEMB -bannerText: Vector set is a new data type that is currently in preview and may be subject to change. --- Return the approximate vector associated with a given element in the vector set. diff --git a/content/commands/vgetattr.md b/content/commands/vgetattr.md index 938b9dc7e6..3cfe7b4c29 100644 --- a/content/commands/vgetattr.md +++ b/content/commands/vgetattr.md @@ -18,7 +18,6 @@ since: 8.0.0 summary: Retrieve the JSON attributes of elements. syntax_fmt: "VGETATTR key element" title: VGETATTR -bannerText: Vector set is a new data type that is currently in preview and may be subject to change. --- Return the JSON attributes associated with an element in a vector set. diff --git a/content/commands/vinfo.md b/content/commands/vinfo.md index a8a77485dc..9c29d75e9a 100644 --- a/content/commands/vinfo.md +++ b/content/commands/vinfo.md @@ -18,7 +18,6 @@ since: 8.0.0 summary: Return information about a vector set. syntax_fmt: "VINFO key" title: VINFO -bannerText: Vector set is a new data type that is currently in preview and may be subject to change. --- Return metadata and internal details about a vector set, including size, dimensions, quantization type, and graph structure. diff --git a/content/commands/vismember.md b/content/commands/vismember.md index 4093d1e32f..84e0cdd04b 100644 --- a/content/commands/vismember.md +++ b/content/commands/vismember.md @@ -28,7 +28,6 @@ summary: Check if an element exists in a vector set. syntax_fmt: VISMEMBER key element syntax_str: element title: VISMEMBER -bannerText: Vector set is a new data type that is currently in preview and may be subject to change. --- Check if an element exists in a vector set. diff --git a/content/commands/vlinks.md b/content/commands/vlinks.md index 4de1fd9091..cd1a53bf8b 100644 --- a/content/commands/vlinks.md +++ b/content/commands/vlinks.md @@ -18,7 +18,6 @@ since: 8.0.0 summary: Return the neighbors of an element at each layer in the HNSW graph. syntax_fmt: "VLINKS key element [WITHSCORES]" title: VLINKS -bannerText: Vector set is a new data type that is currently in preview and may be subject to change. --- Return the neighbors of a specified element in a vector set. The command shows the connections for each layer of the HNSW graph. diff --git a/content/commands/vrandmember.md b/content/commands/vrandmember.md index 26f751464b..ff08c68191 100644 --- a/content/commands/vrandmember.md +++ b/content/commands/vrandmember.md @@ -18,7 +18,6 @@ since: 8.0.0 summary: Return one or multiple random members from a vector set. syntax_fmt: "VRANDMEMBER key [count]" title: VRANDMEMBER -bannerText: Vector set is a new data type that is currently in preview and may be subject to change. --- Return one or more random elements from a vector set. diff --git a/content/commands/vrange.md b/content/commands/vrange.md new file mode 100644 index 0000000000..6261330803 --- /dev/null +++ b/content/commands/vrange.md @@ -0,0 +1,126 @@ +--- +arguments: +- name: key + type: key +- name: start + type: string +- name: end + type: string +- name: count + optional: true + type: integer +arity: -4 +categories: +- docs +- develop +- stack +- oss +- rs +- rc +- oss +- kubernetes +- clients +command_flags: +- READONLY +complexity: O(log(K)+M) where K is the number of elements in the start prefix, and + M is the number of elements returned. In practical terms, the command is just O(M) +description: Return elements in a lexicographical range +function: vrangeCommand +group: vector_set +hidden: false +linkTitle: VRANGE +since: 8.4.0 +summary: Return elements in a lexicographical range +syntax_fmt: VRANGE key start end [count] +syntax_str: start end [count] +title: VRANGE +--- +The `VRANGE` command provides a stateless iterator for the elements inside a vector set. It allows you to retrieve all the elements inside a vector set in small amounts for each call, without an explicit cursor, and with guarantees about what you will miss in case the vector set is changing (elements added and/or removed) during the iteration. + +The command returns elements in lexicographical order, using byte-by-byte comparison (like `memcmp()`) to establish a total order among elements. + +## Required arguments + +
key + +The name of the vector set key from which to retrieve elements. + +
+ +
start + +The starting point of the lexicographical range. Can be: +- A string prefixed with `[` for inclusive range (e.g., `[Redis`) +- A string prefixed with `(` for exclusive range (e.g., `(a7`) +- The special symbol `-` to indicate the minimum element + +
+ +
end + +The ending point of the lexicographical range. Can be: +- A string prefixed with `[` for inclusive range +- A string prefixed with `(` for exclusive range +- The special symbol `+` to indicate the maximum element + +
+ +## Optional arguments + +
count + +The maximum number of elements to return. If `count` is negative, the command returns all elements in the specified range (which may block the server for a long time with large sets). + +
+ +## Examples + +Retrieve the first 10 elements starting from the string "Redis" (inclusive): + +``` +VRANGE word_embeddings [Redis + 10 +``` + +Iterate through all elements, 10 at a time: + +``` +VRANGE mykey - + 10 +``` + +Continue iteration from the last element of the previous result (exclusive): + +``` +VRANGE mykey (a7 + 10 +``` + +Return all elements in the set (use with caution): + +``` +VRANGE mykey - + -1 +``` + +## Return information + +{{< multitabs id="return-info" + tab1="RESP2" + tab2="RESP3" >}} + +One of the following: + +- [Array reply]({{< relref "/develop/reference/protocol-spec#arrays" >}}) of elements in lexicographical order within the specified range. +- [Array reply]({{< relref "/develop/reference/protocol-spec#arrays" >}}) (empty array) if the key doesn't exist. + +-tab-sep- + +One of the following: + +- [Array reply]({{< relref "/develop/reference/protocol-spec#arrays" >}}) of elements in lexicographical order within the specified range. +- [Array reply]({{< relref "/develop/reference/protocol-spec#arrays" >}}) (empty array) if the key doesn't exist. + +{{< /multitabs >}} + +## Behavior + +- **Iteration guarantees**: Each range will produce exactly the elements that were present in the range at the moment the `VRANGE` command was executed. +- **Concurrent modifications**: Elements removed or added during iteration may or may not be returned, depending on when they were modified. +- **Empty key**: If the key doesn't exist, returns an empty array. diff --git a/content/commands/vrem.md b/content/commands/vrem.md index 227c1e65e2..ac5f01bed5 100644 --- a/content/commands/vrem.md +++ b/content/commands/vrem.md @@ -18,7 +18,6 @@ since: 8.0.0 summary: Remove an element from a vector set. syntax_fmt: "VREM key element" title: VREM -bannerText: Vector set is a new data type that is currently in preview and may be subject to change. --- Remove an element from a vector set. diff --git a/content/commands/vsetattr.md b/content/commands/vsetattr.md index 46688b309f..7362819f2c 100644 --- a/content/commands/vsetattr.md +++ b/content/commands/vsetattr.md @@ -18,7 +18,6 @@ since: 8.0.0 summary: Associate or remove the JSON attributes of elements. syntax_fmt: "VSETATTR key element \"{ JSON obj }\"" title: VSETATTR -bannerText: Vector set is a new data type that is currently in preview and may be subject to change. --- Associate a JSON object with an element in a vector set. Use this command to store attributes that can be used in filtered similarity searches with `VSIM`. diff --git a/content/commands/vsim.md b/content/commands/vsim.md index f7bb587ef8..0f8d5564d8 100644 --- a/content/commands/vsim.md +++ b/content/commands/vsim.md @@ -21,7 +21,6 @@ since: 8.0.0 summary: Return elements by vector similarity. syntax_fmt: "VSIM key (ELE | FP32 | VALUES num) (vector | element) [WITHSCORES] [WITHATTRIBS] [COUNT num]\n [EPSILON delta] [EF search-exploration-factor] [FILTER expression] [FILTER-EF max-filtering-effort]\n [TRUTH] [NOTHREAD]" title: VSIM -bannerText: Vector set is a new data type that is currently in preview and may be subject to change. --- Return elements similar to a given vector or element. Use this command to perform approximate or exact similarity searches within a vector set. diff --git a/content/develop/data-types/vector-sets/_index.md b/content/develop/data-types/vector-sets/_index.md index b9046b705f..2baff73bf1 100644 --- a/content/develop/data-types/vector-sets/_index.md +++ b/content/develop/data-types/vector-sets/_index.md @@ -13,8 +13,6 @@ description: Introduction to Redis vector sets linkTitle: Vector sets title: Redis vector sets weight: 55 -bannerText: Vector set is a new data type that is currently in preview and may be subject to change. -bannerChildren: true --- Vector sets are a data type similar to sorted sets, but instead of a score, vector set elements have a string representation of a vector. From d6b44e0bd21a52d3df17100b272e9856cecddc3e Mon Sep 17 00:00:00 2001 From: David Dougherty Date: Mon, 27 Oct 2025 09:15:31 -0700 Subject: [PATCH 04/17] DOC-5575: update the modules API page (#2305) --- .../reference/modules/modules-api-ref.md | 188 ++++++++++++++++-- 1 file changed, 173 insertions(+), 15 deletions(-) diff --git a/content/develop/reference/modules/modules-api-ref.md b/content/develop/reference/modules/modules-api-ref.md index c4aaf91cf9..eeef91966c 100644 --- a/content/develop/reference/modules/modules-api-ref.md +++ b/content/develop/reference/modules/modules-api-ref.md @@ -384,6 +384,8 @@ example "write deny-oom". The set of flags are: * **"internal"**: Internal command, one that should not be exposed to the user connections. For example, module commands that are called by the modules, commands that do not perform ACL validations (relying on earlier checks) +* **"touches-arbitrary-keys"**: This command may modify arbitrary keys (i.e. not provided via argv). + This flag is used so we don't wrap the replicated commands with MULTI/EXEC. The last three parameters specify which arguments of the new command are Redis keys. See [https://redis.io/commands/command](https://redis.io/commands/command) for more information. @@ -5047,9 +5049,11 @@ is interested in. This can be an ORed mask of any of the following flags: - `REDISMODULE_NOTIFY_NEW`: New key notification - `REDISMODULE_NOTIFY_OVERWRITTEN`: Overwritten events - `REDISMODULE_NOTIFY_TYPE_CHANGED`: Type-changed events + - `REDISMODULE_NOTIFY_KEY_TRIMMED`: Key trimmed events after a slot migration operation - `REDISMODULE_NOTIFY_ALL`: All events (Excluding `REDISMODULE_NOTIFY_KEYMISS`, - REDISMODULE_NOTIFY_NEW, REDISMODULE_NOTIFY_OVERWRITTEN - and REDISMODULE_NOTIFY_TYPE_CHANGED) + REDISMODULE_NOTIFY_NEW, REDISMODULE_NOTIFY_OVERWRITTEN, + REDISMODULE_NOTIFY_TYPE_CHANGED + and REDISMODULE_NOTIFY_KEY_TRIMMED) - `REDISMODULE_NOTIFY_LOADED`: A special notification available only for modules, indicates that the key was loaded from persistence. Notice, when this event fires, the given key @@ -5096,7 +5100,7 @@ See [https://redis.io/docs/latest/develop/use/keyspace-notifications/](https://r int types, RedisModuleNotificationFunc callback); -**Available since:** unreleased +**Available since:** 8.2.0 [`RedisModule_UnsubscribeFromKeyspaceEvents`](#RedisModule_UnsubscribeFromKeyspaceEvents) - Unregister a module's callback from keyspace notifications for specific event types. @@ -5341,6 +5345,18 @@ With the following effects: Returns the cluster slot of a key, similar to the `CLUSTER KEYSLOT` command. This function works even if cluster mode is not enabled. + + +### `RedisModule_ClusterKeySlotC` + + unsigned int RedisModule_ClusterKeySlotC(const char *keystr, size_t keylen); + +**Available since:** unreleased + +Like [`RedisModule_ClusterKeySlot`](#RedisModule_ClusterKeySlot), but gets a char pointer and a length. +Returns the cluster slot of a key, similar to the `CLUSTER KEYSLOT` command. +This function works even if cluster mode is not enabled. + ### `RedisModule_ClusterCanonicalKeyNameInSlot` @@ -5353,6 +5369,86 @@ Returns a short string that can be used as a key or as a hash tag in a key, such that the key maps to the given cluster slot. Returns NULL if slot is not a valid slot. + + +### `RedisModule_ClusterCanAccessKeysInSlot` + + int RedisModule_ClusterCanAccessKeysInSlot(int slot); + +**Available since:** unreleased + +Returns 1 if keys in the specified slot can be accessed by this node, 0 otherwise. + +This function returns 1 in the following cases: +- The slot is owned by this node or by its master if this node is a replica +- The slot is being imported under the old slot migration approach (CLUSTER SETSLOT IMPORTING ..) +- Not in cluster mode (all slots are accessible) + +Returns 0 for: +- Invalid slot numbers (< 0 or >= 16384) +- Slots owned by other nodes + + + +### `RedisModule_ClusterPropagateForSlotMigration` + + int RedisModule_ClusterPropagateForSlotMigration(RedisModuleCtx *ctx, + const char *cmdname, + const char *fmt, + ...); + +**Available since:** unreleased + +Propagate commands along with slot migration. + +This function allows modules to add commands that will be sent to the +destination node before the actual slot migration begins. It should only be +called during the `REDISMODULE_SUBEVENT_CLUSTER_SLOT_MIGRATION_MIGRATE_MODULE_PROPAGATE` event. + +This function can be called multiple times within the same event to +replicate multiple commands. All commands will be sent before the +actual slot data migration begins. + +Note: This function is only available in the fork child process just before + slot snapshot delivery begins. + +On success `REDISMODULE_OK` is returned, otherwise +`REDISMODULE_ERR` is returned and errno is set to the following values: + +* EINVAL: function arguments or format specifiers are invalid. +* EBADF: not called in the correct context, e.g. not called in the `REDISMODULE_SUBEVENT_CLUSTER_SLOT_MIGRATION_MIGRATE_MODULE_PROPAGATE` event. +* ENOENT: command does not exist. +* ENOTSUP: command is cross-slot. +* ERANGE: command contains keys that are not within the migrating slot range. + + + +### `RedisModule_ClusterGetLocalSlotRanges` + + RedisModuleSlotRangeArray *RedisModule_ClusterGetLocalSlotRanges(RedisModuleCtx *ctx); + +**Available since:** unreleased + +Returns the locally owned slot ranges for the node. + +An optional `ctx` can be provided to enable auto-memory management. +If cluster mode is disabled, the array will include all slots (0–16383). +If the node is a replica, the slot ranges of its master are returned. + +The returned array must be freed with [`RedisModule_ClusterFreeSlotRanges()`](#RedisModule_ClusterFreeSlotRanges). + + + +### `RedisModule_ClusterFreeSlotRanges` + + void RedisModule_ClusterFreeSlotRanges(RedisModuleCtx *ctx, + RedisModuleSlotRangeArray *slots); + +**Available since:** unreleased + +Frees a slot range array returned by [`RedisModule_ClusterGetLocalSlotRanges()`](#RedisModule_ClusterGetLocalSlotRanges). +Pass the `ctx` pointer only if the array was created with a context. + ## Modules Timers API @@ -7261,6 +7357,63 @@ Here is a list of events you can use as 'eid' and related sub events: RedisModuleKey *key; // Key name +* `RedisModuleEvent_ClusterSlotMigration` + + Called when an atomic slot migration (ASM) event happens. + IMPORT events are triggered on the destination side of a slot migration + operation. These notifications let modules prepare for the upcoming + ownership change, observe successful completion once the cluster config + reflects the new owner, or detect a failure in which case slot ownership + remains with the source. + + Similarly, MIGRATE events triggered on the source side of a slot + migration operation to let modules prepare for the ownership change and + observe the completion of the slot migration. MIGRATE_MODULE_PROPAGATE + event is triggered in the fork just before snapshot delivery; modules may + use it to enqueue commands that will be delivered first. See + RedisModule_ClusterPropagateForSlotMigration() for details. + + * `REDISMODULE_SUBEVENT_CLUSTER_SLOT_MIGRATION_IMPORT_STARTED` + * `REDISMODULE_SUBEVENT_CLUSTER_SLOT_MIGRATION_IMPORT_FAILED` + * `REDISMODULE_SUBEVENT_CLUSTER_SLOT_MIGRATION_IMPORT_COMPLETED` + * `REDISMODULE_SUBEVENT_CLUSTER_SLOT_MIGRATION_MIGRATE_STARTED` + * `REDISMODULE_SUBEVENT_CLUSTER_SLOT_MIGRATION_MIGRATE_FAILED` + * `REDISMODULE_SUBEVENT_CLUSTER_SLOT_MIGRATION_MIGRATE_COMPLETED` + * `REDISMODULE_SUBEVENT_CLUSTER_SLOT_MIGRATION_MIGRATE_MODULE_PROPAGATE` + + The data pointer can be casted to a RedisModuleClusterSlotMigrationInfo + structure with the following fields: + + char source_node_id[REDISMODULE_NODE_ID_LEN + 1]; + char destination_node_id[REDISMODULE_NODE_ID_LEN + 1]; + const char *task_id; // Task ID + RedisModuleSlotRangeArray *slots; // Slot ranges + +* `RedisModuleEvent_ClusterSlotMigrationTrim` + + Called when trimming keys after a slot migration. Fires on the source + after a successful migration to clean up migrated keys, or on the + destination after a failed import to discard partial imports. Two methods + are supported. In the first method, keys are deleted in a background + thread; this is reported via the TRIM_BACKGROUND event. In the second + method, Redis performs incremental deletions on the main thread via the + cron loop to avoid stalls; this is reported via the TRIM_STARTED and + TRIM_COMPLETED events. Each deletion emits REDISMODULE_NOTIFY_KEY_TRIMMED + so modules can react to individual key deletions. Redis selects the + method automatically: background by default; switches to main thread + trimming when a module subscribes to REDISMODULE_NOTIFY_KEY_TRIMMED. + + The following sub events are available: + + * `REDISMODULE_SUBEVENT_CLUSTER_SLOT_MIGRATION_TRIM_STARTED` + * `REDISMODULE_SUBEVENT_CLUSTER_SLOT_MIGRATION_TRIM_COMPLETED` + * `REDISMODULE_SUBEVENT_CLUSTER_SLOT_MIGRATION_TRIM_BACKGROUND` + + The data pointer can be casted to a RedisModuleClusterSlotMigrationTrimInfo + structure with the following fields: + + RedisModuleSlotRangeArray *slots; // Slot ranges + The function returns `REDISMODULE_OK` if the module was successfully subscribed for the specified event. If the API is called from a wrong context or unsupported event is given then `REDISMODULE_ERR` is returned. @@ -7591,7 +7744,7 @@ cluster, and by that gain the permissions to execute internal commands. RedisModuleConfigIterator *RedisModule_ConfigIteratorCreate(RedisModuleCtx *ctx, const char *pattern); -**Available since:** unreleased +**Available since:** 8.2.0 Get an iterator to all configs. Optional `ctx` can be provided if use of auto-memory is desired. @@ -7654,7 +7807,7 @@ the caller is responsible for freeing the iterator using void RedisModule_ConfigIteratorRelease(RedisModuleCtx *ctx, RedisModuleConfigIterator *iter); -**Available since:** unreleased +**Available since:** 8.2.0 Release the iterator returned by [`RedisModule_ConfigIteratorCreate()`](#RedisModule_ConfigIteratorCreate). If auto-memory is enabled and manual release is needed one must pass the same `RedisModuleCtx` @@ -7666,7 +7819,7 @@ that was used to create the iterator. int RedisModule_ConfigGetType(const char *name, RedisModuleConfigType *res); -**Available since:** unreleased +**Available since:** 8.2.0 Get the type of a config as `RedisModuleConfigType`. One may use this in order to get or set the values of the config with the appropriate function if the @@ -7694,7 +7847,7 @@ If a config with the given name exists `res` is populated with its type, else const char *RedisModule_ConfigIteratorNext(RedisModuleConfigIterator *iter); -**Available since:** unreleased +**Available since:** 8.2.0 Go to the next element of the config iterator. @@ -7713,7 +7866,7 @@ See [`RedisModule_ConfigIteratorCreate()`](#RedisModule_ConfigIteratorCreate) fo const char *name, RedisModuleString **res); -**Available since:** unreleased +**Available since:** 8.2.0 Get the value of a config as a string. This function can be used to get the value of any config, regardless of its type. @@ -7730,7 +7883,7 @@ is returned and `res` is populated with the value. int RedisModule_ConfigGetBool(RedisModuleCtx *ctx, const char *name, int *res); -**Available since:** unreleased +**Available since:** 8.2.0 Get the value of a bool config. @@ -7746,7 +7899,7 @@ value. const char *name, RedisModuleString **res); -**Available since:** unreleased +**Available since:** 8.2.0 Get the value of an enum config. @@ -7763,7 +7916,7 @@ string. const char *name, long long *res); -**Available since:** unreleased +**Available since:** 8.2.0 Get the value of a numeric config. @@ -7780,7 +7933,7 @@ value. RedisModuleString *value, RedisModuleString **err); -**Available since:** unreleased +**Available since:** 8.2.0 Set the value of a config. @@ -7800,7 +7953,7 @@ is not NULL, it will be populated with an error message. int value, RedisModuleString **err); -**Available since:** unreleased +**Available since:** 8.2.0 Set the value of a bool config. @@ -7815,7 +7968,7 @@ See [`RedisModule_ConfigSet`](#RedisModule_ConfigSet) for return value. RedisModuleString *value, RedisModuleString **err); -**Available since:** unreleased +**Available since:** 8.2.0 Set the value of an enum config. @@ -7833,7 +7986,7 @@ See [`RedisModule_ConfigSet`](#RedisModule_ConfigSet) for return value. long long value, RedisModuleString **err); -**Available since:** unreleased +**Available since:** 8.2.0 Set the value of a numeric config. If the value passed is meant to be a percentage, it should be passed as a @@ -8346,8 +8499,13 @@ There is no guarantee that this info is always available, so this may return -1. * [`RedisModule_Calloc`](#RedisModule_Calloc) * [`RedisModule_ChannelAtPosWithFlags`](#RedisModule_ChannelAtPosWithFlags) * [`RedisModule_CloseKey`](#RedisModule_CloseKey) +* [`RedisModule_ClusterCanAccessKeysInSlot`](#RedisModule_ClusterCanAccessKeysInSlot) * [`RedisModule_ClusterCanonicalKeyNameInSlot`](#RedisModule_ClusterCanonicalKeyNameInSlot) +* [`RedisModule_ClusterFreeSlotRanges`](#RedisModule_ClusterFreeSlotRanges) +* [`RedisModule_ClusterGetLocalSlotRanges`](#RedisModule_ClusterGetLocalSlotRanges) * [`RedisModule_ClusterKeySlot`](#RedisModule_ClusterKeySlot) +* [`RedisModule_ClusterKeySlotC`](#RedisModule_ClusterKeySlotC) +* [`RedisModule_ClusterPropagateForSlotMigration`](#RedisModule_ClusterPropagateForSlotMigration) * [`RedisModule_CommandFilterArgDelete`](#RedisModule_CommandFilterArgDelete) * [`RedisModule_CommandFilterArgGet`](#RedisModule_CommandFilterArgGet) * [`RedisModule_CommandFilterArgInsert`](#RedisModule_CommandFilterArgInsert) From 989cdef3f4698046be89812b87337754cfe6f170 Mon Sep 17 00:00:00 2001 From: David Dougherty Date: Mon, 27 Oct 2025 09:53:41 -0700 Subject: [PATCH 05/17] DOC-5853: update CLIENT LIST history information (#2306) --- content/commands/client-list.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/content/commands/client-list.md b/content/commands/client-list.md index 5d05e66c04..f4ce6c105a 100644 --- a/content/commands/client-list.md +++ b/content/commands/client-list.md @@ -70,6 +70,12 @@ history: - Added `resp`, `multi-mem`, `rbs` and `rbp` fields. - - 7.0.3 - Added `ssub` field. +- - 7.2.0 + - Added `lib-name` and `lib-ver` fields. +- - 7.4.0 + - Added `watch` field. +- - 8.0.0 + - Added `io-thread` field. linkTitle: CLIENT LIST since: 2.4.0 summary: Lists open connections. From 55aa008bc4ab82e323d11ce3a458a7a353f4265f Mon Sep 17 00:00:00 2001 From: David Dougherty Date: Tue, 28 Oct 2025 13:58:42 -0700 Subject: [PATCH 06/17] DOC-5864: update tested-on information (#2311) --- content/operate/oss_and_stack/install/install-stack/_index.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/content/operate/oss_and_stack/install/install-stack/_index.md b/content/operate/oss_and_stack/install/install-stack/_index.md index b3d9b6cfaa..72580a6ccc 100644 --- a/content/operate/oss_and_stack/install/install-stack/_index.md +++ b/content/operate/oss_and_stack/install/install-stack/_index.md @@ -13,10 +13,10 @@ weight: 2 Redis Open Source has been tested on the following platforms: -- Ubuntu 20.04 (Focal Fossa), 22.04 (Jammy Jellyfish), and 24.04 (Noble Numbat). +- Ubuntu 22.04 (Jammy Jellyfish) and 24.04 (Noble Numbat). - Rocky Linux 8.10 and 9.5. - AlmaLinux 8.10 and 9.5. -- Debian 11 (Bullseye) and 12 (Bookworm). +- Debian 12 (Bookworm) and 13 (Trixie). - macOS 13 (Ventura), 14 (Sonoma), and 15 (Sequoia). Follow the links below for installation instructions for your OS and distribution. From a67f352d3c673029cb73f046f46f97481036db0c Mon Sep 17 00:00:00 2001 From: David Dougherty Date: Thu, 30 Oct 2025 06:34:53 -0700 Subject: [PATCH 07/17] DOC-5800: search: document new vector search attrib. (#2209) * DOC-5800: search: document new vector search attrib. * Remove temp. file * Remove query parm info from FT.SEARCH and FT.AGG... cmd pages * Add reference to the query attributes page * Apply suggestions from code review Co-authored-by: andy-stark-redis <164213578+andy-stark-redis@users.noreply.github.com> --------- Co-authored-by: andy-stark-redis <164213578+andy-stark-redis@users.noreply.github.com> --- content/commands/ft.search.md | 10 +++ .../advanced-concepts/query_syntax.md | 7 +- .../search-and-query/query/vector-search.md | 26 ++++++- .../ai/search-and-query/vectors/_index.md | 75 +++++++++++++++++++ 4 files changed, 116 insertions(+), 2 deletions(-) diff --git a/content/commands/ft.search.md b/content/commands/ft.search.md index 9f3e607f4a..260416fd13 100644 --- a/content/commands/ft.search.md +++ b/content/commands/ft.search.md @@ -679,6 +679,16 @@ Search for books with semantically similar title to _Planet Earth_. Return top 1 {{< / highlight >}} +
+Vector search with cluster optimization + +Search for books with titles that are semantically similar to _Planet Earth_ using cluster optimization. Each shard retrieves 60% of the requested results for improved performance in Redis cluster environments. See the [query attributes]({{< relref "/develop/ai/search-and-query/advanced-concepts/query_syntax#query-attributes" >}}) reference page for more information. + +{{< highlight bash >}} +127.0.0.1:6379> FT.SEARCH books-idx "*=>[KNN 100 @title_embedding $query_vec]=>{$SHARD_K_RATIO: 0.6; $YIELD_DISTANCE_AS: title_score}" PARAMS 2 query_vec <"Planet Earth" embedding BLOB> SORTBY title_score DIALECT 2 +{{< / highlight >}} +
+
Search for a phrase using SLOP diff --git a/content/develop/ai/search-and-query/advanced-concepts/query_syntax.md b/content/develop/ai/search-and-query/advanced-concepts/query_syntax.md index 17189011f7..4fd6c32007 100644 --- a/content/develop/ai/search-and-query/advanced-concepts/query_syntax.md +++ b/content/develop/ai/search-and-query/advanced-concepts/query_syntax.md @@ -389,7 +389,8 @@ The supported attributes are: As of v2.6.1, the query attributes syntax supports these additional attributes: -* **$yield_distance_as**: specifies the distance field name, used for later sorting and/or returning, for clauses that yield some distance metric. It is currently supported for vector queries only (both KNN and range). +* **$yield_distance_as**: specifies the distance field name for clauses that yield some distance metric. This is used for later sorting and/or returning. It is currently supported for vector queries only (both KNN and range). +* **$shard_k_ratio**: controls how many results each shard retrieves relative to the requested top_k in cluster setups. Value range: 0.1 - 1.0 (default: 1.0). Only applicable to vector KNN queries in Redis cluster environments. See [Cluster-specific query parameters]({{< relref "develop/ai/search-and-query/vectors#cluster-specific-query-parameters" >}}) for detailed information. * **vector query params**: pass optional parameters for [vector queries]({{< relref "develop/ai/search-and-query/vectors#querying-vector-fields" >}}) in key-value format. ## A few query examples @@ -458,6 +459,10 @@ As of v2.6.1, the query attributes syntax supports these additional attributes: @age:[(18 +inf] +* Vector search with cluster optimization - retrieve 100 nearest neighbors with each shard providing 50% of results: + + *=>[KNN 100 @doc_embedding $BLOB]=>{$SHARD_K_RATIO: 0.5; $YIELD_DISTANCE_AS: vector_distance} + ## Mapping common SQL predicates to Redis Query Engine | SQL Condition | Redis Query Engine Equivalent | Comments | diff --git a/content/develop/ai/search-and-query/query/vector-search.md b/content/develop/ai/search-and-query/query/vector-search.md index e93a24c40a..aac6829f6c 100644 --- a/content/develop/ai/search-and-query/query/vector-search.md +++ b/content/develop/ai/search-and-query/query/vector-search.md @@ -106,4 +106,28 @@ query = ( .return_fields('vector_dist', 'description') .dialect(2) ) - \ No newline at end of file + + +## Cluster optimization + +In Redis cluster environments, you can optimize vector search performance using the `$SHARD_K_RATIO` query attribute. This parameter controls how many results each shard retrieves relative to the requested `top_k`, creating a tunable trade-off between accuracy and performance. + +### Basic cluster optimization + +Retrieve 100 nearest neighbors with each shard providing 60% of the requested results: + +{{< clients-example query_vector vector3 >}} +FT.SEARCH idx:bikes_vss "(*)=>[KNN 100 @vector $query_vector]=>{$SHARD_K_RATIO: 0.6; $YIELD_DISTANCE_AS: vector_distance}" PARAMS 2 "query_vector" "Z\xf8\x15:\xf23\xa1\xbfZ\x1dI>\r\xca9..." SORTBY vector_distance ASC RETURN 2 "vector_distance" "description" DIALECT 2 +{{< /clients-example >}} + +### Combined with filtering + +You can combine `$SHARD_K_RATIO` with pre-filtering to optimize searches on specific subsets of data: + +{{< clients-example query_vector vector4 >}} +FT.SEARCH idx:bikes_vss "(@brand:trek)=>[KNN 50 @vector $query_vector]=>{$SHARD_K_RATIO: 0.4; $YIELD_DISTANCE_AS: similarity}" PARAMS 2 "query_vector" "Z\xf8\x15:\xf23\xa1\xbfZ\x1dI>\r\xca9..." SORTBY similarity ASC RETURN 2 "similarity" "description" DIALECT 2 +{{< /clients-example >}} + +{{% alert title="Note" color="warning" %}} +The `$SHARD_K_RATIO` parameter is only applicable in Redis cluster environments and has no effect in standalone Redis instances. +{{% /alert %}} \ No newline at end of file diff --git a/content/develop/ai/search-and-query/vectors/_index.md b/content/develop/ai/search-and-query/vectors/_index.md index f824f4a684..59990a1266 100644 --- a/content/develop/ai/search-and-query/vectors/_index.md +++ b/content/develop/ai/search-and-query/vectors/_index.md @@ -414,6 +414,24 @@ By default, Redis selects the best filter mode to optimize query execution. You | `HYBRID_POLICY` | Specifies the filter mode to use during vector search with filters (hybrid). | `BATCHES` or `ADHOC_BF` | | `BATCH_SIZE` | A fixed batch size to use in every iteration when the `BATCHES` policy is auto-selected or requested. | Positive integer. | +### Cluster-specific query parameters + +**$SHARD_K_RATIO** + +The `$SHARD_K_RATIO` parameter controls how many results each shard retrieves relative to the requested `top_k` in Redis cluster environments. This creates a tunable trade-off between accuracy and performance. + +| Parameter | Description | Value Range | Default | +|:-----------------|:------------|:------------|:--------| +| `$SHARD_K_RATIO` | Ratio of `top_k` results to fetch per shard in cluster setups. Calculation: `shard_results = top_k × ratio`. | 0.1 - 1.0 (up to 2 decimal places) | 1.0 | + +**Important considerations:** + +- Only applicable to vector KNN queries in Redis cluster environments +- Each shard must retrieve at least `max(top_k / #shards, ceil(top_k × ratio))` results +- If the user-defined ratio is lower than `top_k / #shards`, the server ignores it and uses the minimum required ratio +- Unbalanced shards returning fewer results than required may lead to fewer total results than requested in `top_k` +- Invalid ratios return descriptive error messages + ### Index-specific query parameters @@ -523,6 +541,63 @@ FT.SEARCH movies "(@category:{action})=>[KNN 10 @doc_embedding $BLOB]=>{$HYBRID_ To explore additional Python vector search examples, review recipes for the [`Redis Python`](https://github.com/redis-developer/redis-ai-resources/blob/main/python-recipes/vector-search/00_redispy.ipynb) client library and the [`Redis Vector Library`](https://github.com/redis-developer/redis-ai-resources/blob/main/python-recipes/vector-search/01_redisvl.ipynb). +### Cluster optimization examples + +The following examples demonstrate using `$SHARD_K_RATIO` to optimize vector search performance in Redis cluster environments. + +Return the top 100 nearest neighbors with each shard providing 50% of the requested results (50 results per shard): + +``` +FT.SEARCH documents "*=>[KNN 100 @doc_embedding $BLOB]=>{$SHARD_K_RATIO: 0.5; $YIELD_DISTANCE_AS: vector_distance}" PARAMS 2 BLOB "\x12\xa9\xf5\x6c" SORTBY vector_distance DIALECT 2 +``` + +Combine cluster optimization with filtering and other query attributes. Among movies with `action` category, return top 50 nearest neighbors with 30% shard ratio and custom EF_RUNTIME: + +``` +FT.SEARCH movies "(@category:{action})=>[KNN 50 @movie_embedding $BLOB]=>{$SHARD_K_RATIO: 0.3; $EF_RUNTIME: 150; $YIELD_DISTANCE_AS: movie_distance}" PARAMS 4 BLOB "\x12\xa9\xf5\x6c" EF 150 SORTBY movie_distance DIALECT 2 +``` + +Use a higher ratio for better accuracy when precision is more important than performance: + +``` +FT.SEARCH products "*=>[KNN 20 @product_embedding $BLOB]=>{$SHARD_K_RATIO: 0.8; $YIELD_DISTANCE_AS: similarity}" PARAMS 2 BLOB "\x12\xa9\xf5\x6c" SORTBY similarity DIALECT 2 +``` + +### Cluster considerations and best practices + +When using `$SHARD_K_RATIO` in Redis cluster environments, consider the following behavioral characteristics and best practices: + +#### Minimum results guarantee + +Each shard must retrieve at least `max(top_k / #shards, ceil(top_k × ratio))` results to ensure the cluster can return the requested `top_k` results. If your specified ratio would result in fewer results per shard than this minimum, Redis automatically adjusts to the minimum required ratio. + +**Example scenarios:** +- `top_k=100`, 4 shards, `ratio=0.5` → 50 results per shard (valid) +- `top_k=100`, 4 shards, `ratio=0.2` → 25 results per shard (minimum: 25, so valid) +- `top_k=100`, 8 shards, `ratio=0.1` → 10 results per shard (minimum: 13, so Redis uses 13) + +#### Handling unbalanced shards + +In cases where some shards contain fewer documents than required to fulfill the `top_k` results, using a lower ratio may lead to fewer total results than requested. To mitigate this: + +1. **Monitor shard distribution**: Ensure your data is relatively balanced across shards +2. **Adjust ratio dynamically**: If you consistently get fewer results than expected, increase the ratio +3. **Use higher ratios for critical queries**: Do this when result completeness is more important than performance + +#### Performance vs accuracy trade-offs + +| Ratio Range | Use Case | Performance | Accuracy | +|:------------|:---------|:------------|:---------| +| 0.1 - 0.3 | High-performance, approximate results | Excellent | Good | +| 0.4 - 0.6 | Balanced performance and accuracy | Good | Very Good | +| 0.7 - 1.0 | High-accuracy, precision-critical | Fair | Excellent | + +#### Error handling + +Redis returns descriptive error messages for invalid `$SHARD_K_RATIO` values: +- Values outside the 0.1 - 1.0 range +- Values with more than 2 decimal places +- Non-numeric values ### Range query examples From 4036f61d294d23fee4773de43bb81827e3e74922 Mon Sep 17 00:00:00 2001 From: David Dougherty Date: Mon, 3 Nov 2025 06:26:23 -0800 Subject: [PATCH 08/17] DOC-5717: update XREADGROUP command page (#2312) --- content/commands/xreadgroup.md | 44 ++++++++++++++++++++++++++++++---- 1 file changed, 40 insertions(+), 4 deletions(-) diff --git a/content/commands/xreadgroup.md b/content/commands/xreadgroup.md index 94916c2776..bd18be2671 100644 --- a/content/commands/xreadgroup.md +++ b/content/commands/xreadgroup.md @@ -25,6 +25,11 @@ arguments: optional: true token: BLOCK type: integer +- display_text: min-idle-time + name: min-idle-time + optional: true + token: CLAIM + type: integer - display_text: noack name: noack optional: true @@ -85,8 +90,8 @@ since: 5.0.0 summary: Returns new or historical messages from a stream for a consumer in a group. Blocks until a message is available otherwise. syntax_fmt: "XREADGROUP GROUP\_group consumer [COUNT\_count] [BLOCK\_milliseconds]\n\ - \ [NOACK] STREAMS\_key [key ...] id [id ...]" -syntax_str: "[COUNT\_count] [BLOCK\_milliseconds] [NOACK] STREAMS\_key [key ...] id\ + \ [CLAIM\_min-idle-time] [NOACK] STREAMS\_key [key ...] id [id ...]" +syntax_str: "[COUNT\_count] [BLOCK\_milliseconds] [CLAIM\_min-idle-time] [NOACK] STREAMS\_key [key ...] id\ \ [id ...]" title: XREADGROUP --- @@ -140,16 +145,47 @@ can be inspected using the [`XPENDING`]({{< relref "/commands/xpending" >}}) com The `NOACK` subcommand can be used to avoid adding the message to the PEL in cases where reliability is not a requirement and the occasional message loss is acceptable. This is equivalent to acknowledging the message when it is read. +When used together with `CLAIM`, `NOACK` does not apply for retrieved pending entries. The ID to specify in the **STREAMS** option when using `XREADGROUP` can be one of the following two: -* The special `>` ID, which means that the consumer want to receive only messages that were *never delivered to any other consumer*. It just means, give me new messages. -* Any other ID, that is, 0 or any other valid ID or incomplete ID (just the millisecond time part), will have the effect of returning entries that are pending for the consumer sending the command with IDs greater than the one provided. So basically if the ID is not `>`, then the command will just let the client access its pending entries: messages delivered to it, but not yet acknowledged. Note that in this case, both `BLOCK` and `NOACK` are ignored. +* When the special `>` id is specified without `CLAIM`, the consumer wants to receive only messages that were never delivered to any other consumer, hence, just new messages. +* When the special `>` id is specified with `CLAIM`, the consumer wants, in addition, to receive messages that have been delivered to some consumer and have been pending for at least min-idle-time milliseconds. +* Any other ID, that is, 0 or any other valid ID or incomplete ID (just the millisecond time part), will have the effect of returning entries that are pending for the consumer sending the command with IDs greater than the one provided. So basically if the ID is not `>`, then the command will just let the client access its pending entries: messages delivered to it, but not yet acknowledged. Note that in this case, `BLOCK`, `NOACK`, and `CLAIM` are ignored. Like [`XREAD`]({{< relref "/commands/xread" >}}) the `XREADGROUP` command can be used in a blocking way. There are no differences in this regard. +## The CLAIM option + +When `CLAIM min-idle-time` is specified, Redis will first try to claim messages which have been pending for at least min-idle-time milliseconds from the consumer group of each specified stream key. The pending messages with the highest idle time would be claimed first. Note that the `CLAIM min-idle-time` condition may become true for some pending entries during the `BLOCK milliseconds` period (if specified). + +If there are no such messages, Redis will continue as normal (consume incoming messages). + +`CLAIM min-idle-time` is ignored if the specified id is not `>`. + +### Reply extension for claimed entries + +When `CLAIM min-idle-time` is used, additional information is provided for each pending entry retrieved (similar to the reply of [`XPENDING`]({{< relref "/commands/xpending" >}})). For each claimed pending entry, the reply includes: + +1. Entry id +2. Field-value pairs +3. Milliseconds since the last time this entry was delivered +4. Number of times this entry was previously delivered + +### Ordering guarantees + +When using `CLAIM`, the following ordering guarantees apply: + +- Idle pending entries are reported first, then incoming entries +- Idle pending entries are ordered by idle time (longer first) +- Incoming entries are ordered by the order they were appended to the stream (older first) + +For example, if there are 20 idle pending entries and 200 incoming entries (in all the specified streams together): +- When calling `XREADGROUP ... CLAIM ...`, you would retrieve 220 entries in the reply +- When calling `XREADGROUP ... COUNT 100 ... CLAIM ...`, you would retrieve the 20 idle pending entries + 80 incoming entries in the reply + ## What happens when a message is delivered to a consumer? Two things: From a96281ff7332d2223abfcabc3904a554426e7682 Mon Sep 17 00:00:00 2001 From: David Dougherty Date: Mon, 3 Nov 2025 07:23:59 -0800 Subject: [PATCH 09/17] DOC-5720: add new MSETEX command page (#2310) * DOC-5720: add new MSETEX command page * Apply code review suggestions --- content/commands/msetex.md | 150 +++++++++++++++++++++++++++++++++++++ 1 file changed, 150 insertions(+) create mode 100644 content/commands/msetex.md diff --git a/content/commands/msetex.md b/content/commands/msetex.md new file mode 100644 index 0000000000..ac7d2d5a0f --- /dev/null +++ b/content/commands/msetex.md @@ -0,0 +1,150 @@ +--- +acl_categories: +- '@write' +- '@string' +- '@slow' +arguments: +- display_text: numkeys + name: numkeys + type: integer +- arguments: + - display_text: key + key_spec_index: 0 + name: key + type: key + - display_text: value + name: value + type: string + multiple: true + name: data + type: block +- arguments: + - display_text: nx + name: nx + token: NX + type: pure-token + - display_text: xx + name: xx + token: XX + type: pure-token + name: condition + optional: true + type: oneof +- arguments: + - display_text: seconds + name: seconds + token: EX + type: integer + - display_text: milliseconds + name: milliseconds + token: PX + type: integer + - display_text: unix-time-seconds + name: unix-time-seconds + token: EXAT + type: unix-time + - display_text: unix-time-milliseconds + name: unix-time-milliseconds + token: PXAT + type: unix-time + - display_text: keepttl + name: keepttl + token: KEEPTTL + type: pure-token + name: expiration + optional: true + type: oneof +arity: -4 +categories: +- docs +- develop +- stack +- oss +- rs +- rc +- oss +- kubernetes +- clients +command_flags: +- write +- denyoom +- movablekeys +complexity: O(N) where N is the number of keys to set. +description: Atomically sets multiple string keys with a shared expiration in a single + operation. Supports flexible argument parsing where condition and expiration flags + can appear in any order. +group: string +hidden: false +hints: +- request_policy:multi_shard +- response_policy:all_succeeded +key_specs: +- OW: true + begin_search: + spec: + index: 1 + type: index + find_keys: + spec: + firstkey: 1 + keynumidx: 0 + keystep: 2 + type: keynum + update: true +linkTitle: MSETEX +since: 8.4.0 +summary: Atomically sets multiple string keys with a shared expiration in a single + operation. Supports flexible argument parsing where condition and expiration flags + can appear in any order. +syntax_fmt: "MSETEX numkeys key value [key value ...] [NX | XX] [EX\_seconds |\n \ + \ PX\_milliseconds | EXAT\_unix-time-seconds |\n PXAT\_unix-time-milliseconds |\ + \ KEEPTTL]" +syntax_str: "key value [key value ...] [NX | XX] [EX\_seconds | PX\_milliseconds |\ + \ EXAT\_unix-time-seconds | PXAT\_unix-time-milliseconds | KEEPTTL]" +title: MSETEX +--- +Atomically sets multiple string keys with an optional shared expiration in a single operation. Supports flexible argument parsing where condition and expiration flags can appear in any order. + +## Required arguments + +
numkeys + +The number of keys being set. + +
+ +
key value [key value ...] + +A series of key/value pairs. + +
+ +## Optional arguments + +
condition and expiration flags + +The `MSETEX` command supports a set of options that modify its behavior: + +* `NX` -- Set the keys and their expiration time only if none of the specified keys exist. +* `XX` -- Set the keys and their expiration time only if all of the specified keys already exist. +* `EX seconds` -- Set the specified expiration time in seconds (a positive integer). +* `PX milliseconds` -- Set the specified expiration time in milliseconds (a positive integer). +* `EXAT unix-time-seconds` -- Set the specified Unix time in seconds (a positive integer) at which the key(s) will expire. +* `PXAT unix-time-milliseconds` -- Set the specified Unix time in milliseconds (a positive integer) at which the key(s) will expire. +* `KEEPTTL` -- Retain the time to live associated with the keys. + +
+ +## Return information + +{{< multitabs id="return-info" + tab1="RESP2" + tab2="RESP3" >}} + +[Integer reply]({{< relref "/develop/reference/protocol-spec#integers" >}}): 0 if none of the keys were set; 1 if all of the keys were set. + +-tab-sep- + +[Integer reply]({{< relref "/develop/reference/protocol-spec#integers" >}}): 0 if none of the keys were set; 1 if all of the keys were set. + +{{< /multitabs >}} From 4b14a6862458a0a1a0d9ec6b630332fc9bcaf735 Mon Sep 17 00:00:00 2001 From: David Dougherty Date: Mon, 3 Nov 2025 09:13:47 -0800 Subject: [PATCH 10/17] DOC-5718: add new string commands (#2307) * Initial commit * DOC-5718: add new string commands * Apply code review suggestions * Apply more code review suggestions * Apply yet more code review suggestions --- content/commands/delex.md | 106 +++++++++++++++++++++++++++++++++++++ content/commands/digest.md | 76 ++++++++++++++++++++++++++ content/commands/set.md | 69 ++++++++++++++++++------ 3 files changed, 235 insertions(+), 16 deletions(-) create mode 100644 content/commands/delex.md create mode 100644 content/commands/digest.md diff --git a/content/commands/delex.md b/content/commands/delex.md new file mode 100644 index 0000000000..9e27399a92 --- /dev/null +++ b/content/commands/delex.md @@ -0,0 +1,106 @@ +--- +acl_categories: +- '@write' +- '@string' +- '@fast' +arguments: +- display_text: key + key_spec_index: 0 + name: key + type: key +- arguments: + - display_text: ifeq-value + name: ifeq-value + token: IFEQ + type: string + - display_text: ifne-value + name: ifne-value + token: IFNE + type: string + - display_text: ifdeq-digest + name: ifdeq-digest + token: IFDEQ + type: integer + - display_text: ifdne-digest + name: ifdne-digest + token: IFDNE + type: integer + name: condition + optional: true + type: oneof +arity: -2 +categories: +- docs +- develop +- stack +- oss +- rs +- rc +- oss +- kubernetes +- clients +command_flags: +- write +- fast +complexity: O(1) for IFEQ/IFNE, O(N) for IFDEQ/IFDNE where N is the length of the string value. +description: Conditionally removes the specified key based on value or hash digest comparison. +group: string +hidden: false +key_specs: +- RW: true + begin_search: + spec: + index: 1 + type: index + delete: true + find_keys: + spec: + keystep: 1 + lastkey: 0 + limit: 0 + type: range +linkTitle: DELEX +since: 8.4.0 +summary: Conditionally removes the specified key based on value or hash digest comparison. +syntax_fmt: "DELEX key [IFEQ\_ifeq-value | IFNE\_ifne-value | IFDEQ\_ifdeq-digest\ + \ |\n IFDNE\_ifdne-digest]" +syntax_str: "[IFEQ\_ifeq-value | IFNE\_ifne-value | IFDEQ\_ifdeq-digest | IFDNE\_\ + ifdne-digest]" +title: DELEX +--- + +Conditionally removes the specified key based on value or hash digest comparison. + +## Hash Digest + +A hash digest is a fixed-size numerical representation of a string value, computed using the XXH3 hash algorithm. Redis uses this hash digest for efficient comparison operations without needing to compare the full string content. You can retrieve a key's hash digest using the [`DIGEST`]({{< relref "/commands/digest" >}}) command, which returns it as a hexadecimal string that you can use with the `IFDEQ` and `IFDNE` options, and also the [`SET`]({{< relref "/commands/set" >}}) command's `IFDEQ` and `IFDNE` options. + +## Options + +The DELEX command supports a set of options that modify its behavior. +Only one of the options can be specified. + +* `IFEQ ifeq-value` -- Remove the key if the value is equal to the specified value. +* `IFNE ifne-value` -- Remove the key if the value is not equal to the specified value. +* `IFDEQ ifeq-digest` -- Remove the key if its hash digest is equal to the specified hash digest. +* `IFDNE ifne-digest` -- Remove the key if its hash digest is not equal to the specified hash digest. + +In 8.4, keys must be of type string when using one of the options above. If no options are specified, the key is removed regardless of its type. + +## Return information + +{{< multitabs id="return-info" + tab1="RESP2" + tab2="RESP3" >}} + +One of the following: +- [Integer reply]({{< relref "/develop/reference/protocol-spec#integers" >}}): 0 if not deleted (the key does not exist or a specified `IFEQ/IFNE/IFDEQ/IFDNE` condition is false), or 1 if deleted. +- [Simple error reply]({{< relref "/develop/reference/protocol-spec#simple-errors" >}}) if the key exists but holds a value that is not a string and one of `IFEQ, IFNE, IFDEQ,` or `IFDNE` is specified. + +-tab-sep- + +One of the following: +- [Integer reply]({{< relref "/develop/reference/protocol-spec#integers" >}}): 0 if not deleted (the key does not exist or a specified `IFEQ/IFNE/IFDEQ/IFDNE` condition is false), or 1 if deleted. +- [Simple error reply]({{< relref "/develop/reference/protocol-spec#simple-errors" >}}) if the key exists but holds a value that is not a string and one of `IFEQ, IFNE, IFDEQ,` or `IFDNE` is specified. + +{{< /multitabs >}} diff --git a/content/commands/digest.md b/content/commands/digest.md new file mode 100644 index 0000000000..553ac16956 --- /dev/null +++ b/content/commands/digest.md @@ -0,0 +1,76 @@ +--- +acl_categories: +- '@read' +- '@string' +- '@fast' +arguments: +- display_text: key + key_spec_index: 0 + name: key + type: key +arity: 2 +categories: +- docs +- develop +- stack +- oss +- rs +- rc +- oss +- kubernetes +- clients +command_flags: +- readonly +- fast +complexity: O(N) where N is the length of the string value. +description: Returns the hash digest of a string value. +group: string +hidden: false +key_specs: +- RO: true + access: true + begin_search: + spec: + index: 1 + type: index + find_keys: + spec: + keystep: 1 + lastkey: 0 + limit: 0 + type: range +linkTitle: DIGEST +since: 8.4.0 +summary: Returns the hash digest of a string value as a hexadecimal string. +syntax_fmt: DIGEST key +syntax_str: '' +title: DIGEST +--- + +Get the hash digest for the value stored in the specified key as a hexadecimal string. Keys must be of type string. + +## Hash Digest + +A hash digest is a fixed-size numerical representation of a string value, computed using the XXH3 hash algorithm. Redis uses this hash digest for efficient comparison operations without needing to compare the full string content. You can use these hash digests with the [SET]({{< relref "/commands/set" >}}) command's `IFDEQ` and `IFDNE` options, and also the [DELEX]({{< relref "/commands/delex" >}}) command's `IFDEQ` and `IFDNE` options. + +## Return information + +{{< multitabs id="return-info" + tab1="RESP2" + tab2="RESP3" >}} + +One of the following: + +- [Null bulk string reply]({{< relref "/develop/reference/protocol-spec#bulk-strings" >}}) if the key does not exist. +- [Simple error reply]({{< relref "/develop/reference/protocol-spec#simple-errors" >}}) if the key exists but holds a value which is not a string. +- [Bulk string reply]({{< relref "/develop/reference/protocol-spec#bulk-strings" >}}) the hash digest of the value stored in the key as a hexadecimal string. + +-tab-sep- + +One of the following: + +- [Null bulk string reply]({{< relref "/develop/reference/protocol-spec#bulk-strings" >}}) if the key does not exist. +- [Simple error reply]({{< relref "/develop/reference/protocol-spec#simple-errors" >}}) if the key exists but holds a value which is not a string. +- [Bulk string reply]({{< relref "/develop/reference/protocol-spec#bulk-strings" >}}) the hash digest of the value stored in the key as a hexadecimal string. + +{{< /multitabs >}} diff --git a/content/commands/set.md b/content/commands/set.md index 30e453a06d..abddbc2d43 100644 --- a/content/commands/set.md +++ b/content/commands/set.md @@ -20,6 +20,26 @@ arguments: name: xx token: XX type: pure-token + - display_text: ifeq-value + name: ifeq-value + since: 8.4.0 + token: IFEQ + type: string + - display_text: ifne-value + name: ifne-value + since: 8.4.0 + token: IFNE + type: string + - display_text: ifdeq-digest + name: ifdeq-digest + since: 8.4.0 + token: IFDEQ + type: integer + - display_text: ifdne-digest + name: ifdne-digest + since: 8.4.0 + token: IFDNE + type: integer name: condition optional: true since: 2.6.12 @@ -87,6 +107,8 @@ history: - Added the `GET`, `EXAT` and `PXAT` option. - - 7.0.0 - Allowed the `NX` and `GET` options to be used together. +- - 8.4.0 + - Added 'IFEQ', 'IFNE', 'IFDEQ', 'IFDNE' options. key_specs: - RW: true access: true @@ -107,12 +129,15 @@ linkTitle: SET since: 1.0.0 summary: Sets the string value of a key, ignoring its type. The key is created if it doesn't exist. -syntax_fmt: "SET key value [NX | XX] [GET] [EX\_seconds | PX\_milliseconds |\n EXAT\_\ - unix-time-seconds | PXAT\_unix-time-milliseconds | KEEPTTL]" -syntax_str: "value [NX | XX] [GET] [EX\_seconds | PX\_milliseconds | EXAT\_unix-time-seconds\ +syntax_fmt: "SET key value [NX | XX | IFEQ\_ifeq-value | IFNE\_ifne-value |\n\ + \ IFDEQ\_ifdeq-digest | IFDNE\_ifdne-digest] [GET] [EX\_seconds |\n PX\_milliseconds\ + \ | EXAT\_unix-time-seconds |\n PXAT\_unix-time-milliseconds | KEEPTTL]" +syntax_str: "value [NX | XX | IFEQ\_ifeq-value | IFNE\_ifne-value | IFDEQ\_ifdeq-digest\ + \ | IFDNE\_ifdne-digest] [GET] [EX\_seconds | PX\_milliseconds | EXAT\_unix-time-seconds\ \ | PXAT\_unix-time-milliseconds | KEEPTTL]" title: SET --- + Set `key` to hold the string `value`. If `key` already holds a value, it is overwritten, regardless of its type. Any previous time to live associated with the key is discarded on successful `SET` operation. @@ -121,17 +146,25 @@ Any previous time to live associated with the key is discarded on successful `SE The `SET` command supports a set of options that modify its behavior: +* `NX` -- Only set the key if it does not already exist. +* `XX` -- Only set the key if it already exists. +* `IFEQ ifeq-value` -- Set the key’s value and expiration only if its current value is equal to `ifeq-value`. If the key doesn’t exist, it won’t be created. +* `IFNE ifne-value` -- Set the key’s value and expiration only if its current value is not equal to `ifne-value`. If the key doesn’t exist, it will be created. +* `IFDEQ ifeq-digest` -- Set the key’s value and expiration only if the hash digest of its current value is equal to `ifeq-digest`. If the key doesn’t exist, it won’t be created. See the [Hash Digest](#hash-digest) section below for more information. +* `IFDNE ifne-digest` -- Set the key’s value and expiration only if the hash digest of its current value is not equal to `ifne-digest`. If the key doesn’t exist, it will be created. See the [Hash Digest](#hash-digest) section below for more information. +* `GET` -- Return the old string stored at key, or nil if key did not exist. An error is returned and `SET` aborted if the value stored at key is not a string. * `EX` *seconds* -- Set the specified expire time, in seconds (a positive integer). * `PX` *milliseconds* -- Set the specified expire time, in milliseconds (a positive integer). * `EXAT` *timestamp-seconds* -- Set the specified Unix time at which the key will expire, in seconds (a positive integer). * `PXAT` *timestamp-milliseconds* -- Set the specified Unix time at which the key will expire, in milliseconds (a positive integer). -* `NX` -- Only set the key if it does not already exist. -* `XX` -- Only set the key if it already exists. * `KEEPTTL` -- Retain the time to live associated with the key. -* `GET` -- Return the old string stored at key, or nil if key did not exist. An error is returned and `SET` aborted if the value stored at key is not a string. Note: Since the `SET` command options can replace [`SETNX`]({{< relref "/commands/setnx" >}}), [`SETEX`]({{< relref "/commands/setex" >}}), [`PSETEX`]({{< relref "/commands/psetex" >}}), [`GETSET`]({{< relref "/commands/getset" >}}), it is possible that in future versions of Redis these commands will be deprecated and finally removed. +## Hash Digest + +A hash digest is a fixed-size numerical representation of a string value, computed using the XXH3 hash algorithm. Redis uses this hash digest for efficient comparison operations without needing to compare the full string content. You can retrieve a key's hash digest using the [`DIGEST`]({{< relref "/commands/digest" >}}) command, which returns it as a hexadecimal string that you can use with the `IFDEQ` and `IFDNE` options, and also the [`DELEX`]({{< relref "/commands/delex" >}}) command's `IFDEQ` and `IFDNE` options. + ## Examples {{% redis-cli %}} @@ -180,20 +213,24 @@ The script should be called with `EVAL ...script... 1 resource-name token-value` tab1="RESP2" tab2="RESP3" >}} -* If `GET` was not specified, any of the following: - * [Nil reply](../../develop/reference/protocol-spec#bulk-strings): Operation was aborted (conflict with one of the `XX`/`NX` options). The key was not set. +* If `GET` was not specified, one of the following: + * [Null bulk string reply](../../develop/reference/protocol-spec#bulk-strings) in the following two cases. + * The key doesn’t exist and `XX/IFEQ/IFDEQ` was specified. The key was not created. + * The key exists, and `NX` was specified or a specified `IFEQ/IFNE/IFDEQ/IFDNE` condition is false. The key was not set. * [Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`: The key was set. -* If `GET` was specified, any of the following: - * [Nil reply](../../develop/reference/protocol-spec#bulk-strings): The key didn't exist before the `SET`. If `XX` was specified, the key was not set. Otherwise, the key was set. - * [Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): The previous value of the key. If `NX` was specified, the key was not set. Otherwise, the key was set. +* If `GET` was specified, one of the following: + * [Null bulk string reply](../../develop/reference/protocol-spec#bulk-strings): The key didn't exist before the `SET` operation, whether the key was created of not. + * [Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): The previous value of the key, whether the key was set or not. -tab-sep- -* If `GET` was not specified, any of the following: - * [Null reply](../../develop/reference/protocol-spec#nulls): Operation was aborted (conflict with one of the `XX`/`NX` options). The key was not set. +* If `GET` was not specified, one of the following: + * [Null reply](../../develop/reference/protocol-spec#nulls) in the following two cases. + * The key doesn’t exist and `XX/IFEQ/IFDEQ` was specified. The key was not created. + * The key exists, and `NX` was specified or a specified `IFEQ/IFNE/IFDEQ/IFDNE` condition is false. The key was not set. * [Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`: The key was set. -* If `GET` was specified, any of the following: - * [Null reply](../../develop/reference/protocol-spec#nulls): The key didn't exist before the `SET`. If `XX` was specified, the key was not set. Otherwise, the key was set. - * [Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): The previous value of the key. If `NX` was specified, the key was not set. Otherwise, the key was set. +* If `GET` was specified, one of the following: + * [Null reply](../../develop/reference/protocol-spec#nulls): The key didn't exist before the `SET` operation, whether the key was created of not. + * [Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): The previous value of the key, whether the key was set or not. {{< /multitabs >}} From 4f63939b243a461ca6392d7e56c09f34621aa5e6 Mon Sep 17 00:00:00 2001 From: David Dougherty Date: Mon, 3 Nov 2025 13:15:59 -0800 Subject: [PATCH 11/17] DOC-5867: add new CLUSTER MIGRATION command page (etc) (#2314) * DOC-5867: add new CLUSTER MIGRATION command page (etc) * Apply code review suggestions * Apply code review suggestions --- content/commands/client-list.md | 4 +- content/commands/cluster-countkeysinslot.md | 3 + content/commands/cluster-getkeysinslot.md | 3 + content/commands/cluster-info.md | 10 + content/commands/cluster-migration.md | 199 ++++++++++++++++++++ content/commands/info.md | 7 +- 6 files changed, 223 insertions(+), 3 deletions(-) create mode 100644 content/commands/cluster-migration.md diff --git a/content/commands/client-list.md b/content/commands/client-list.md index f4ce6c105a..e7cac5ef0d 100644 --- a/content/commands/client-list.md +++ b/content/commands/client-list.md @@ -135,9 +135,11 @@ b: the client is waiting in a blocking operation c: connection to be closed after writing entire reply d: a watched keys has been modified - EXEC will fail e: the client is excluded from the client eviction mechanism +g: the client is responsible for migrating slots (atomic slot migration) i: the client is waiting for a VM I/O (deprecated) M: the client is a master N: no specific flag set +o: the client is responsible for importing slots (atomic slot migration) O: the client is a client in MONITOR mode P: the client is a Pub/Sub subscriber r: the client is in readonly mode against a cluster node @@ -148,7 +150,7 @@ x: the client is in a MULTI/EXEC context t: the client enabled keys tracking in order to perform client side caching T: the client will not touch the LRU/LFU of the keys it accesses R: the client tracking target client is invalid -B: the client enabled broadcast tracking mode +B: the client enabled broadcast tracking mode ``` The file descriptor events can be: diff --git a/content/commands/cluster-countkeysinslot.md b/content/commands/cluster-countkeysinslot.md index 40e0db713f..fb680c388c 100644 --- a/content/commands/cluster-countkeysinslot.md +++ b/content/commands/cluster-countkeysinslot.md @@ -34,6 +34,9 @@ command only queries the local data set, so contacting a node that is not serving the specified hash slot will always result in a count of zero being returned. +{{< note>}}During atomic slot migration operations (available since Redis 8.4.0), keys being imported or trimmed will be filtered out from the results. +{{< /note >}} + ``` > CLUSTER COUNTKEYSINSLOT 7000 (integer) 50341 diff --git a/content/commands/cluster-getkeysinslot.md b/content/commands/cluster-getkeysinslot.md index 8985ca79d1..880c0fc527 100644 --- a/content/commands/cluster-getkeysinslot.md +++ b/content/commands/cluster-getkeysinslot.md @@ -44,6 +44,9 @@ node to another. The way the rehashing is performed is exposed in the Redis Cluster specification, or in a more simple to digest form, as an appendix of the [`CLUSTER SETSLOT`]({{< relref "/commands/cluster-setslot" >}}) command documentation. +{{< note >}}During atomic slot migration operations (available since Redis 8.4.0), keys being imported or trimmed will be filtered out from the results. +{{< /note >}} + ``` > CLUSTER GETKEYSINSLOT 7000 3 1) "key_39015" diff --git a/content/commands/cluster-info.md b/content/commands/cluster-info.md index 4d83b462c6..add7d7454a 100644 --- a/content/commands/cluster-info.md +++ b/content/commands/cluster-info.md @@ -58,6 +58,16 @@ total_cluster_links_buffer_limit_exceeded:0 * `cluster_stats_messages_received`: Number of messages received via the cluster node-to-node binary bus. * `total_cluster_links_buffer_limit_exceeded`: Accumulated count of cluster links freed due to exceeding the `cluster-link-sendbuf-limit` configuration. +The following atomic slot migration fields may be included in the reply (available since Redis 8.4.0): + +* `cluster_slot_migration_active_tasks`: Number of in-progress ASM tasks. Currently, it will be 1 or 0. +* `cluster_slot_migration_active_trim_running`: Number of active trim jobs in progress and scheduled. +* `cluster_slot_migration_active_trim_current_job_keys`: Number of keys scheduled for deletion in the current trim job. +* `cluster_slot_migration_active_trim_current_job_trimmed`: Number of keys already deleted in the current trim job. +* `cluster_slot_migration_stats_active_trim_started`: Total number of trim jobs that have started since the process began. +* `cluster_slot_migration_stats_active_trim_completed`: Total number of trim jobs completed since the process began. +* `cluster_slot_migration_stats_active_trim_cancelled`: Total number of trim jobs cancelled since the process began. + The following message-related fields may be included in the reply if the value is not 0: Each message type includes statistics on the number of messages sent and received. Here are the explanation of these fields: diff --git a/content/commands/cluster-migration.md b/content/commands/cluster-migration.md new file mode 100644 index 0000000000..06d18f34b6 --- /dev/null +++ b/content/commands/cluster-migration.md @@ -0,0 +1,199 @@ +--- +acl_categories: +- '@admin' +- '@slow' +- '@dangerous' +arguments: +- arguments: + - arguments: + - display_text: start-slot + name: start-slot + type: integer + - display_text: end-slot + name: end-slot + type: integer + multiple: true + name: import + token: IMPORT + type: block + - arguments: + - display_text: task-id + name: task-id + token: ID + type: string + - display_text: all + name: all + token: ALL + type: pure-token + name: cancel + token: CANCEL + type: oneof + - arguments: + - display_text: task-id + name: task-id + optional: true + token: ID + type: string + - display_text: all + name: all + optional: true + token: ALL + type: pure-token + name: status + token: STATUS + type: block + name: subcommand + type: oneof +arity: -4 +categories: +- docs +- develop +- stack +- oss +- rs +- rc +- oss +- kubernetes +- clients +command_flags: +- admin +- stale +- no_async_loading +complexity: O(N) where N is the total number of the slots between the start slot and + end slot arguments. +description: Start, monitor, and cancel atomic slot migration tasks. +group: cluster +hidden: false +linkTitle: CLUSTER MIGRATION +since: 8.4.0 +summary: Start, monitor, and cancel atomic slot migration tasks. +syntax_fmt: "CLUSTER MIGRATION | STATUS\_" +syntax_str: '' +title: CLUSTER MIGRATION +--- + +The `CLUSTER MIGRATION` command provides atomic slot migration functionality for Redis Cluster. This command allows you to import slots from other nodes, monitor the progress of migration tasks, and cancel ongoing migrations. + +## Required arguments + +
subcommand + +The subcommand specifies the operation to perform: + +- `IMPORT [ ...]`: Executes on the destination master. Accepts multiple slot ranges and triggers atomic migration for the specified ranges. Returns a task ID that you can use to monitor the status of the task. + +- `CANCEL | ALL>`: Cancels an ongoing migration task by its ID or cancels all tasks if `ALL` is specified. Note: Cancelling a task on the source node does not stop the migration on the destination node, which will continue retrying until it is also cancelled there. + +- `STATUS [ID | ALL]`: Displays the status of current and completed atomic slot migration tasks. If a specific task ID is provided, it returns detailed information for that task only. If `ALL` is specified, it returns the status of all ongoing and completed tasks. + +
+ +## Examples + +Import slots 0-1000 and 2000-3000 to the current node: + +```bash +CLUSTER MIGRATION IMPORT 0 1000 2000 3000 +``` + +Check the status of all migration tasks: + +```bash +CLUSTER MIGRATION STATUS ALL +``` + +Check the status of a specific migration task: + +```bash +CLUSTER MIGRATION STATUS ID 24cf41718b20f7f05901743dffc40bc9b15db339 +``` + +Cancel a specific migration task: + +```bash +CLUSTER MIGRATION CANCEL ID 24cf41718b20f7f05901743dffc40bc9b15db339 +``` + +Cancel all migration tasks: + +```bash +CLUSTER MIGRATION CANCEL ALL +``` + +## Return information + +{{< multitabs id="return-info" + tab1="RESP2" + tab2="RESP3" >}} + +For the `IMPORT` subcommand: +[Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): task ID on success, or error message on failure. + +For the `CANCEL` subcommand: +[Integer reply](../../develop/reference/protocol-spec#integers): number of cancelled tasks. + +For the `STATUS` subcommand: +[Array reply](../../develop/reference/protocol-spec#arrays): a list of migration task details. Each task is represented as an array containing field-value pairs: +- `id`: Task identifier +- `slots`: Slot range being imported or migrated +- `source`: Source node ID +- `dest`: Destination node ID +- `operation`: Operation type ("import" or "migrate") +- `state`: Current state ("completed", "in_progress", etc.) +- `last_error`: Last error message (empty if none) +- `retries`: Number of retry attempts +- `create_time`: Task creation timestamp +- `start_time`: Task start timestamp +- `end_time`: Task completion timestamp (if completed) +- `write_pause_ms`: Write pause duration in milliseconds + +-tab-sep- + +For the `IMPORT` subcommand: +[Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): task ID on success, or error message on failure. + +For the `CANCEL` subcommand: +[Integer reply](../../develop/reference/protocol-spec#integers): number of cancelled tasks. + +For the `STATUS` subcommand: +[Array reply](../../develop/reference/protocol-spec#arrays): a list of migration task details. Each task is represented as an array containing field-value pairs: +- `id`: Task identifier +- `slots`: Slot range being migrated +- `source`: Source node ID +- `dest`: Destination node ID +- `operation`: Operation type (typically "migrate") +- `state`: Current state ("completed", "in_progress", etc.) +- `last_error`: Last error message (empty if none) +- `retries`: Number of retry attempts +- `create_time`: Task creation timestamp +- `start_time`: Task start timestamp +- `end_time`: Task completion timestamp (if completed) +- `write_pause_ms`: Write pause duration in milliseconds + +{{< /multitabs >}} + +## Notes + +- The atomic slot migration feature is available starting from Redis 8.4.0. +- Cancelling a task on the source node does not automatically stop the migration on the destination node. +- In `CLUSTER MIGRATION STATUS` output, the "state" field will show `completed` for successful operations. +- Tasks with empty "last_error" fields indicate no errors occurred during the migration process. + +## Key visibility during migration + +During atomic slot migration operations, keys in unowned slotsmay be filtered out from the following commands while importing or trimming is in progress: + +- [`KEYS`]({{< relref "/commands/keys" >}}) +- [`SCAN`]({{< relref "/commands/scan" >}}) +- [`RANDOMKEY`]({{< relref "/commands/randomkey" >}}) +- [`CLUSTER GETKEYSINSLOT`]({{< relref "/commands/cluster-getkeysinslot" >}}) +- [`DBSIZE`]({{< relref "/commands/dbsize" >}}) +- [`CLUSTER COUNTKEYSINSLOT`]({{< relref "/commands/cluster-countkeysinslot" >}}) + +The [`INFO KEYSPACE`]({{< relref "/commands/info" >}}) command will continue to reflect the actual number of keys, including those being imported. + +## Related configuration + +- `cluster-slot-migration-handoff-max-lag-bytes`: After slot snapshot completion, if remaining replication stream size falls below this threshold, the source node pauses writes to hand off slot ownership. Higher values trigger handoff earlier but may cause longer write pauses. Lower values result in shorter write pauses but may be harder to reach with steady incoming writes (default: 1MB). +- `cluster-slot-migration-write-pause-timeout`: Maximum duration that the source node pauses writes during ASM handoff. If the destination fails to take over slots within this timeout, the source assumes migration failed and resumes writes (default: 10 seconds). diff --git a/content/commands/info.md b/content/commands/info.md index 142334345f..73ab61df7a 100644 --- a/content/commands/info.md +++ b/content/commands/info.md @@ -199,6 +199,9 @@ Here is the meaning of all fields in the **memory** section: * `mem_clients_slaves`: Memory used by replica clients - Starting Redis 7.0, replica buffers share memory with the replication backlog, so this field can show 0 when replicas don't trigger an increase of memory usage. * `mem_clients_normal`: Memory used by normal clients * `mem_cluster_links`: Memory used by links to peers on the cluster bus when cluster mode is enabled. +* `mem_cluster_slot_migration_output_buffer`: Memory usage of the migration client's output buffer. Redis writes incoming changes to this buffer during the migration process. +* `mem_cluster_slot_migration_input_buffer`: Memory usage of the accumulated replication stream buffer on the importing node. +* `mem_cluster_slot_migration_input_buffer_peak`: Peak accumulated repl buffer size on the importing side. * `mem_aof_buffer`: Transient memory used for AOF and AOF rewrite buffers * `mem_replication_backlog`: Memory used by replication backlog * `mem_total_replication_buffers`: Total memory consumed for replication buffers - Added in Redis 7.0. @@ -501,9 +504,9 @@ The **sentinel** section is only available in Redis Sentinel instances. It consi * `sentinel_scripts_queue_length`: The length of the queue of user scripts that are pending execution * `sentinel_simulate_failure_flags`: Flags for the `SENTINEL SIMULATE-FAILURE` command -The **cluster** section currently only contains a unique field: +The **cluster** section contains a single fields: -* `cluster_enabled`: Indicate Redis cluster is enabled +* `cluster_enabled`: Indicates whether Redis cluster is enabled. The **modules** section contains additional information about loaded modules if the modules provide it. The field part of property lines in this section are always prefixed with the module's name. From 7efe18cec791ece0f72635ce1754f6fc421a9939 Mon Sep 17 00:00:00 2001 From: David Dougherty Date: Tue, 4 Nov 2025 10:54:25 -0800 Subject: [PATCH 12/17] DOC-5574: add 8.4-RC1 release notes (#2344) * DOC-5574: add 8.4-RC1 release notes * Update content/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.4-release-notes.md Co-authored-by: mich-elle-luna <153109578+mich-elle-luna@users.noreply.github.com> --------- Co-authored-by: mich-elle-luna <153109578+mich-elle-luna@users.noreply.github.com> --- .../redisce/redisos-8.4-release-notes.md | 88 +++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 content/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.4-release-notes.md diff --git a/content/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.4-release-notes.md b/content/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.4-release-notes.md new file mode 100644 index 0000000000..08d2f845c5 --- /dev/null +++ b/content/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.4-release-notes.md @@ -0,0 +1,88 @@ +--- +Title: Redis Open Source 8.4 release notes +alwaysopen: false +categories: +- docs +- operate +- stack +description: Redis Open Source 8.4 release notes. +linkTitle: v8.4.0 (November 2025) +min-version-db: blah +min-version-rs: blah +weight: 15 +--- + +## Redis Open Source 8.4-RC1 (November 2025) + +This is the first Release Candidate of Redis 8.4 in Redis Open Source. + +Release Candidates are feature-complete pre-releases. Pre-releases are not suitable for production use. + +### Binary distributions + +- Alpine and Debian Docker images - https://hub.docker.com/_/redis +- Install using snap - see https://github.com/redis/redis-snap +- Install using brew - see https://github.com/redis/homebrew-redis +- Install using RPM - see https://github.com/redis/redis-rpm +- Install using Debian APT - see https://github.com/redis/redis-debian + +### Redis 8.4 was tested on the following operating systems + +- Ubuntu 22.04 (Jammy Jellyfish), 24.04 (Noble Numbat) +- Rocky Linux 8.10, 9.5 +- AlmaLinux 8.10, 9.5 +- Debian 12 (Bookworm), Debian 13 (Trixie) +- macOS 13 (Ventura), 14 (Sonoma), 15 (Sequoia) + +### New Features (compared to 8.2.3) + +- [#14414](https://github.com/redis/redis/pull/14414) New command: `CLUSTER MIGRATION` - atomic slot migration. +- [#14435](https://github.com/redis/redis/pull/14435) New commands: `DELEX` and `DIGEST`; `SET` extensions - atomic compare-and-set and compare-and-delete for string keys. +- [#14434](https://github.com/redis/redis/pull/14434) New command: `MSETEX` - set multiple keys and update their expiration. +- [#14402](https://github.com/redis/redis/pull/14402) `XREADGROUP` - add `CLAIM min-idle-time` to consume both idle pending entries and incoming entries. +- [#14058](https://github.com/redis/redis/pull/14058) Add auto-repair options for broken AOF tail on startup. +- [#14296](https://github.com/redis/redis/pull/14296) Support decoding JSON empty array as a Lua array. +- RedisTimeSeries/RedisTimeSeries[#1773](https://github.com/redistimeseries/redistimeseries/pull/1773) `HELP` and `COMMAND DOCS` now support time series commands (MOD-8133). +- RedisBloom/RedisBloom[#892](https://github.com/redisbloom/redisbloom/pull/892) `HELP` and `COMMAND DOCS` now support probabilistic commands (MOD-8133). +- RediSearch/RediSearch[#7076](https://github.com/redisearch/redisearch/pull/7076), RediSearch/RediSearch[#6857](https://github.com/redisearch/redisearch/pull/6857) New Command: `FT.HYBRID` - hybrid queries with RRF and LINEAR combination. +- RediSearch/RediSearch[#7022](https://github.com/redisearch/redisearch/pull/7022) Support index updates when atomic slot migrations occur. +- RediSearch/RediSearch[#6313](https://github.com/redisearch/redisearch/pull/6313) Support multiple I/O threads for RQE cluster manager - Coordinator (MOD-10562). + +### Bug fixes (compared to 8.2.3) + +- [#14423](https://github.com/redis/redis/pull/14423) Potential infinite loop when a stream is corrupted. +- [#14420](https://github.com/redis/redis/pull/14420) Shutdown blocked client not being properly reset after shutdown cancellation. +- [#14417](https://github.com/redis/redis/pull/14417) `CLUSTER FORGET` - heap-buffer-overflow. +- [#14415](https://github.com/redis/redis/pull/14415) Potential crash in `lookupKey()` when `executing_client` is NULL. +- RedisTimeSeries/RedisTimeSeries[#1776](https://github.com/redistimeseries/redistimeseries/pull/1776) Potential crash on `TS.RANGE` with `ALIGN +`, `AGGREGATION twa` and `EMPTY` (MOD-11620, MOD-10484). + +### Performance and resource utilization improvements (compared to 8.2.3) + +- [#14440](https://github.com/redis/redis/pull/14440) Lookahead prefetching - parse multiple commands in advance through a lookahead pipeline. +- [#14309](https://github.com/redis/redis/pull/14309) Optimize `BITCOUNT` with AVX2 and AVX512 popcount implementations. +- [#14227](https://github.com/redis/redis/pull/14227) Optimize `BITCOUNT` with Arm Neon SIMD vectorization. +- [#14428](https://github.com/redis/redis/pull/14428) Optimize HyperLogLog with branchless comparisons and Arm Neon SIMD vectorization. +- [#14222](https://github.com/redis/redis/pull/14222) Optimize Vector set `VADD` and `VSIM` with AVX2 and AVX512 dot product implementations. +- RedisJSON/ijson[#9](https://github.com/redisjson/redisjson/pull/9) JSON - memory footprint improvement by using homogeneous arrays (MOD-9511). +- RedisJSON/ijson[#7](https://github.com/redisjson/redisjson/pull/7) JSON - memory footprint improvement by inlining short strings (MOD-9511). + +### Configuration parameters + +- [#14058](https://github.com/redis/redis/pull/14058) `aof-load-corrupt-tail-max-size` - maximum corrupted tail size (in bytes) to attempt to repair automatically. +- [#14296](https://github.com/redis/redis/pull/14296) `decode_array_with_array_mt` - Lua: control how empty JSON arrays are handled. +- [#14440](https://github.com/redis/redis/pull/14440) `lookahead` - runtime-configurable lookahead depth (default: 16). +- RediSearch/RediSearch[#7065](https://github.com/redisearch/redisearch/pull/7065) `search-default-scorer` - default text and tag scorer (new default is BM25STD). +- RediSearch/RediSearch[#6769](https://github.com/redisearch/redisearch/pull/6769) `search-on-oom` - behavior when OOM event occurs in the query time, supports 3 values: + - `IGNORE` - queries run despite OOM, not recommended for heavy result sets (current behaviour). + - `FAIL` - query execution fails if any node is in OOM state at start. + - `RETURN` - returns partial results if OOM is detected in only some cluster nodes (default). +- RediSearch/RediSearch[#6313](https://github.com/redisearch/redisearch/pull/6313) `search-io-threads` - allow setting the comms threads used by the cluster manager - coordinator (default: 20). + +### Known bugs and limitations + +- When executing `FT.SEARCH`, `FT.AGGREGATE`, `FT.CURSOR`, `FT.HYBRID`, `TS.MGET`, `TS.MRANGE`, `TS.MREVRANGE` and `TS.QUERYINDEX` while an atomic slot migration process is in progress, the results may be partial or contain duplicates. +- `FT.PROFILE`, `FT.EXPLAIN` and `FT.EXPLACINCLI` don’t contain the `FT.HYBRID` option. +- Metrics from `FT.HYBRID` command aren’t displayed on `FT.INFO` and `INFO`. +- Option `EXPLAINSCORE`, `SHARD_K_RATIO`, `YIELD_DISTANCE_AS`, and `WITHCURSOR` with `FT.HYBRID` are not available +- Post-filtering (after `COMBINE` step) using FILTER is not available. +- Currently, the default response format considers only `key_id` and `score`, this may change for delivering the entire document content. From ba8c23ae7da812731dc807c948fdfc54af85f910 Mon Sep 17 00:00:00 2001 From: David Dougherty Date: Tue, 4 Nov 2025 12:14:18 -0800 Subject: [PATCH 13/17] DOC-5577: update what's new page with 8.4 info (#2345) --- content/develop/whats-new/8-4.md | 106 ++++++++++++++++++ .../develop/whats-new/redis-feature-sets.md | 1 + 2 files changed, 107 insertions(+) create mode 100644 content/develop/whats-new/8-4.md diff --git a/content/develop/whats-new/8-4.md b/content/develop/whats-new/8-4.md new file mode 100644 index 0000000000..3771d519e1 --- /dev/null +++ b/content/develop/whats-new/8-4.md @@ -0,0 +1,106 @@ +--- +title: Redis 8.4 +alwaysopen: false +categories: +- docs +- operate +- rs +- rc +description: What's new in Redis 8.4 RC1 +linkTitle: What's new in Redis 8.4 RC1 +weight: 3 +--- + +Redis 8.4 builds on the foundation of Redis 8.2 with significant enhancements to cluster operations, string manipulation, and stream processing capabilities. + +This release delivers major improvements across multiple areas: +- Atomic cluster slot migration for zero-downtime operations +- Enhanced string operations with atomic compare-and-set functionality +- Advanced stream processing with idle entry claiming +- Hybrid search capabilities combining multiple ranking algorithms +- High-performance SIMD optimizations for bit operations and vector processing +- Improved JSON handling with better memory efficiency + +Below is a detailed breakdown of these updates. + +## New features + +### Atomic cluster operations + +Redis 8.4 introduces the [`CLUSTER MIGRATION`]({{< relref "/commands/cluster-migration/" >}}) command, which enables atomic slot migration between cluster nodes. This command ensures zero-downtime cluster operations by moving slots and their associated data in a single atomic operation, eliminating the brief unavailability window that previously occurred during slot migrations. + +### Enhanced string operations + +Redis 8.4 extends string manipulation capabilities with powerful new commands that enable atomic compare-and-set operations: + +- [`DELEX`]({{< relref "/commands/delex/" >}}): Delete a key only if its value matches a specified condition. +- [`DIGEST`]({{< relref "/commands/digest/" >}}): Generate cryptographic digests of key values for integrity verification. + +The [`SET`]({{< relref "/commands/set/" >}}) command now supports atomic compare-and-set and compare-and-delete operations through new extensions, enabling you to implement lock-free data structures and optimistic concurrency control patterns. + +### Multi-key expiration management + +The new [`MSETEX`]({{< relref "/commands/msetex/" >}}) command allows you to set multiple keys and update their expiration times in a single atomic operation. This simplifies batch operations where you need to maintain consistent TTL values across related keys. + +### Advanced stream processing + +Redis 8.4 enhances stream processing with the `CLAIM min-idle-time` option for [`XREADGROUP`]({{< relref "/commands/xreadgroup/" >}}). This feature enables consumer groups to automatically claim and process both idle pending entries and new incoming entries in a single operation, simplifying consumer group management and improving processing efficiency. + +### Hybrid search capabilities + +The new `FT.HYBRID` command introduces hybrid queries that combine multiple ranking algorithms using Reciprocal Rank Fusion (RRF) and linear combination methods. This enables you to create sophisticated search experiences that leverage both semantic similarity and traditional text matching. + +### Enhanced AOF reliability + +Redis 8.4 introduces auto-repair options for corrupted AOF (Append Only File) tails during startup. The new `aof-load-corrupt-tail-max-size` configuration parameter allows Redis to automatically repair minor AOF corruption, improving system resilience and reducing manual intervention requirements. + +## Improvements + +Redis 8.4 delivers substantial performance improvements through advanced SIMD optimizations and enhanced algorithms. + +### Performance optimizations + +- **BITCOUNT**: Enhanced with AVX2 and AVX512 popcount implementations for x86 architectures, plus Arm Neon SIMD vectorization for ARM processors. +- **HyperLogLog**: Optimized with branchless comparisons and Arm Neon SIMD vectorization for improved cardinality estimation performance. +- **Vector operations**: `VADD` and `VSIM` commands benefit from AVX2 and AVX512 dot product implementations for faster vector processing. +- **Command processing**: New lookahead prefetching parses multiple commands in advance through a lookahead pipeline, reducing processing latency. + +### Memory efficiency improvements + +- **JSON data types**: Improved memory footprint through homogeneous array optimization and short string inlining. +- **Lua integration**: Enhanced JSON array handling with the new `decode_array_with_array_mt` configuration parameter for better memory utilization. + +### Search and indexing enhancements + +Redis 8.4 introduces several improvements to search functionality: + +- **Default scoring**: The new `search-default-scorer` parameter sets BM25STD as the default text and tag scorer, providing better relevance ranking out of the box. +- **OOM handling**: The `search-on-oom` parameter controls query behavior during out-of-memory conditions, with options to ignore, fail, or return partial results. +- **Multi-threading**: The `search-io-threads` parameter allows you to configure communication threads for cluster manager coordination, improving search performance in clustered environments. +- **Index updates**: Search indexes now support updates during atomic slot migrations, maintaining search functionality during cluster operations. + +### Enhanced configuration + +Redis 8.4 introduces new configuration parameters that give you greater control over system behavior: + +- `lookahead`: Runtime-configurable lookahead depth for command prefetching (default: 16) +- `aof-load-corrupt-tail-max-size`: Maximum corrupted tail size for automatic AOF repair +- `decode_array_with_array_mt`: Controls how Lua handles empty JSON arrays + +## Component versions + +Redis 8.4 continues the unified distribution approach, delivering all functionality in a single Redis Open Source package without separate modules. This includes: + +- **RedisTimeSeries**: Enhanced with `HELP` and `COMMAND DOCS` support for time series commands +- **RedisBloom**: Improved with `HELP` and `COMMAND DOCS` support for probabilistic commands +- **RediSearch**: Advanced hybrid search capabilities and improved cluster coordination + +## Known limitations + +When using Redis 8.4, be aware of these current limitations: + +- Search commands (`FT.SEARCH`, `FT.AGGREGATE`, `FT.CURSOR`, `FT.HYBRID`) and time series commands (`TS.MGET`, `TS.MRANGE`, `TS.MREVRANGE`, `TS.QUERYINDEX`) may return partial results or duplicates during atomic slot migration. +- `FT.PROFILE`, `FT.EXPLAIN`, and `FT.EXPLAINCLI` don't include `FT.HYBRID` options. +- `FT.HYBRID` metrics aren't displayed in `FT.INFO` and `INFO` commands. +- Several `FT.HYBRID` options (`EXPLAINSCORE`, `SHARD_K_RATIO`, `YIELD_DISTANCE_AS`, `WITHCURSOR`) are not yet available. +- Post-filtering after the `COMBINE` step using `FILTER` is not currently supported. diff --git a/content/develop/whats-new/redis-feature-sets.md b/content/develop/whats-new/redis-feature-sets.md index f0782471e2..ddfc5d6552 100644 --- a/content/develop/whats-new/redis-feature-sets.md +++ b/content/develop/whats-new/redis-feature-sets.md @@ -17,6 +17,7 @@ To use a new feature introduced in a later feature set, you must upgrade the cor | Redis feature set | What's new | |-------------------|------------| +| **Feature set version:** 8.4| See [here]({{< relref "/develop/whats-new/8-4" >}})| | **Feature set version:** 8.2| See [here]({{< relref "/develop/whats-new/8-2" >}})| | **Feature set version:** 8.0| See [here]({{< relref "/develop/whats-new/8-0" >}})| | **Feature set version:** 7.4

**Component versions:**
[Redis 7.4]({{}})
[Search 2.10]({{< relref "/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.10-release-notes.md" >}})
[JSON 2.8]({{}})
[Time series 1.12]({{}})
[Bloom 2.8]({{}}) | **Hash**:
- [Expiration of individual hash fields]({{}}).
**Streams**:
- To start reading from the last stream message, use [`XREAD`]({{}}) with the new ID value `+`.
**Time series**:
Insertion-filter for close samples.
**JSON**:
- A fix to not duplicate `AOF` commands multiple times in [`JSON.MSET`]({{< relref "commands/json.mset/" >}}).
**Probabilistic**:
- Returns an error if [`CMS.MERGE`]({{< relref "commands/cms.merge/" >}}) results in an overflow or underflow.
**Redis Query Engine**:
- New `BFLOAT16` and `FLOAT16` vector data types, reducing memory consumed by vectors while preserving accuracy.
- Support for indexing empty and missing values and enhanced developer experience for queries with exact matching capabilities.
- You can match `TAG` fields without needing to escape special characters.
- Expanded geospatial search with new `INTERSECT` and `DISJOINT` operators, improved reporting of the memory consumed by the index, and exposed full-text scoring in aggregation pipelines. | From 7a43e3fb85910b79a68a0ff2d8e9598315f447cd Mon Sep 17 00:00:00 2001 From: David Dougherty Date: Tue, 4 Nov 2025 12:15:48 -0800 Subject: [PATCH 14/17] DOC-5863: update config. params pages (#2346) --- content/operate/oss_and_stack/management/config.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/content/operate/oss_and_stack/management/config.md b/content/operate/oss_and_stack/management/config.md index d72df73b93..14305163ab 100644 --- a/content/operate/oss_and_stack/management/config.md +++ b/content/operate/oss_and_stack/management/config.md @@ -43,6 +43,8 @@ The list of configuration directives, and their meaning and intended usage is available in the self-documented example `redis.conf` or `redis-full.conf` shipped into the Redis distribution. +* The self-documented [redis-full.conf for Redis 8.4](https://raw.githubusercontent.com/redis/redis/8.4/redis-full.conf). +* The self-documented [redis-full.conf for Redis 8.2](https://raw.githubusercontent.com/redis/redis/8.2/redis-full.conf). * The self-documented [redis-full.conf for Redis 8.0](https://raw.githubusercontent.com/redis/redis/8.0/redis-full.conf). * The self-documented [redis.conf for Redis 7.4](https://raw.githubusercontent.com/redis/redis/7.4/redis.conf). * The self-documented [redis.conf for Redis 7.2](https://raw.githubusercontent.com/redis/redis/7.2/redis.conf). From ff8335193012e535e3d4d165539877aeb618debc Mon Sep 17 00:00:00 2001 From: David Dougherty Date: Wed, 5 Nov 2025 10:08:11 -0800 Subject: [PATCH 15/17] DOC-5589: update RS/RC compat. info. for new cmds (#2352) --- content/commands/cluster-migration.md | 6 ++++++ content/commands/delex.md | 7 +++++++ content/commands/digest.md | 6 ++++++ content/commands/msetex.md | 6 ++++++ 4 files changed, 25 insertions(+) diff --git a/content/commands/cluster-migration.md b/content/commands/cluster-migration.md index 06d18f34b6..05ab684f43 100644 --- a/content/commands/cluster-migration.md +++ b/content/commands/cluster-migration.md @@ -121,6 +121,12 @@ Cancel all migration tasks: CLUSTER MIGRATION CANCEL ALL ``` +## Redis Software and Redis Cloud compatibility + +| Redis
Enterprise | Redis
Cloud | Notes | +|:----------------------|:-----------------|:------| +| ❌ Standard
❌ Active-Active | ❌ Standard
❌ Active-Active | | + ## Return information {{< multitabs id="return-info" diff --git a/content/commands/delex.md b/content/commands/delex.md index 9e27399a92..817ceb24bc 100644 --- a/content/commands/delex.md +++ b/content/commands/delex.md @@ -87,6 +87,13 @@ Only one of the options can be specified. In 8.4, keys must be of type string when using one of the options above. If no options are specified, the key is removed regardless of its type. +## Redis Software and Redis Cloud compatibility + +| Redis
Enterprise | Redis
Cloud | Notes | +|:----------------------|:-----------------|:------| +| ❌ Standard
❌ Active-Active | ❌ Standard
❌ Active-Active | | + + ## Return information {{< multitabs id="return-info" diff --git a/content/commands/digest.md b/content/commands/digest.md index 553ac16956..9e336dd83b 100644 --- a/content/commands/digest.md +++ b/content/commands/digest.md @@ -53,6 +53,12 @@ Get the hash digest for the value stored in the specified key as a hexadecimal s A hash digest is a fixed-size numerical representation of a string value, computed using the XXH3 hash algorithm. Redis uses this hash digest for efficient comparison operations without needing to compare the full string content. You can use these hash digests with the [SET]({{< relref "/commands/set" >}}) command's `IFDEQ` and `IFDNE` options, and also the [DELEX]({{< relref "/commands/delex" >}}) command's `IFDEQ` and `IFDNE` options. +## Redis Software and Redis Cloud compatibility + +| Redis
Enterprise | Redis
Cloud | Notes | +|:----------------------|:-----------------|:------| +| ❌ Standard
❌ Active-Active | ❌ Standard
❌ Active-Active | | + ## Return information {{< multitabs id="return-info" diff --git a/content/commands/msetex.md b/content/commands/msetex.md index ac7d2d5a0f..4613476efa 100644 --- a/content/commands/msetex.md +++ b/content/commands/msetex.md @@ -135,6 +135,12 @@ The `MSETEX` command supports a set of options that modify its behavior:
+## Redis Software and Redis Cloud compatibility + +| Redis
Enterprise | Redis
Cloud | Notes | +|:----------------------|:-----------------|:------| +| ❌ Standard
❌ Active-Active | ❌ Standard
❌ Active-Active | | + ## Return information {{< multitabs id="return-info" From 88a25c731cc6b6ad4072902606de43fd9207e514 Mon Sep 17 00:00:00 2001 From: David Dougherty Date: Fri, 7 Nov 2025 06:44:15 -0800 Subject: [PATCH 16/17] DOC-5924: update final bits and pieces for 8.4 release (#2353) * DOC-5924: update final bits and pieces for 8.4 release * update two commands per JSON files --- content/commands/tdigest.add.md | 2 +- content/commands/topk.add.md | 4 ++-- content/develop/reference/modules/modules-api-ref.md | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/content/commands/tdigest.add.md b/content/commands/tdigest.add.md index 9412d1d884..96cd99b143 100644 --- a/content/commands/tdigest.add.md +++ b/content/commands/tdigest.add.md @@ -22,7 +22,7 @@ categories: - oss - kubernetes - clients -complexity: O(N) , where N is the number of samples to add +complexity: O(N), where N is the number of samples to add description: Adds one or more observations to a t-digest sketch group: tdigest hidden: false diff --git a/content/commands/topk.add.md b/content/commands/topk.add.md index 8932b0bd50..d7b046ca8d 100644 --- a/content/commands/topk.add.md +++ b/content/commands/topk.add.md @@ -20,14 +20,14 @@ categories: - kubernetes - clients complexity: O(n * k) where n is the number of items and k is the depth -description: Increases the count of one or more items by increment +description: Adds an item to a Top-k sketch. Multiple items can be added at the same time. group: topk hidden: false linkTitle: TOPK.ADD module: Bloom since: 2.0.0 stack_path: docs/data-types/probabilistic -summary: Increases the count of one or more items by increment +summary: Adds an item to a Top-k sketch. Multiple items can be added at the same time. syntax_fmt: TOPK.ADD key items [items ...] syntax_str: items [items ...] title: TOPK.ADD diff --git a/content/develop/reference/modules/modules-api-ref.md b/content/develop/reference/modules/modules-api-ref.md index eeef91966c..d5ffcb0fcc 100644 --- a/content/develop/reference/modules/modules-api-ref.md +++ b/content/develop/reference/modules/modules-api-ref.md @@ -7991,6 +7991,7 @@ See [`RedisModule_ConfigSet`](#RedisModule_ConfigSet) for return value. Set the value of a numeric config. If the value passed is meant to be a percentage, it should be passed as a negative value. +For unsigned configs, pass the value and cast to (long long) - internal type checks will handle it. See [`RedisModule_ConfigSet`](#RedisModule_ConfigSet) for return value. From 04ff38abda2aeac83e858e6a671713aa9dfda1e0 Mon Sep 17 00:00:00 2001 From: David Dougherty Date: Fri, 7 Nov 2025 14:01:44 -0800 Subject: [PATCH 17/17] DOC-5801: search: add new FT.HYBRID command (#2210) * DOC-5801: search: add new FT.HYBRID command * Add compat. info * Fixed commands.json parsing; updated FT.HYBRID syntax * Apply suggestions from code review Co-authored-by: adrianoamaral * Update content/commands/ft.hybrid.md Co-authored-by: adrianoamaral * Fix build error and provide more copy-editing * Fix compat. heading --------- Co-authored-by: adrianoamaral --- build/components/syntax.py | 15 +- content/commands/ft.hybrid.md | 614 ++++++++++++++++++++++++++++++++++ 2 files changed, 627 insertions(+), 2 deletions(-) create mode 100644 content/commands/ft.hybrid.md diff --git a/build/components/syntax.py b/build/components/syntax.py index 39afe53651..8b63ee904a 100644 --- a/build/components/syntax.py +++ b/build/components/syntax.py @@ -20,6 +20,12 @@ class ArgumentType(Enum): BLOCK = 'block' PURE_TOKEN = 'pure-token' COMMAND = 'command' + FUNCTION = 'function' + INDEX = 'index' + KEYNUM = 'keynum' + KEYWORD = 'keyword' + RANGE = 'range' + UNKNOWN = 'unknown' class Argument: @@ -28,8 +34,8 @@ def __init__(self, data: dict = {}, level: int = 0, max_width: int = 640) -> Non self._stack = [] self._level: int = level self._max_width: int = max_width - self._name: str = data['name'] - self._type = ArgumentType(data['type']) + self._name: str = data.get('name', data.get('token', 'unnamed')) + self._type = ArgumentType(data.get('type', 'string')) self._optional: bool = data.get('optional', False) self._multiple: bool = data.get('multiple', False) self._multiple_token: bool = data.get('multiple_token', False) @@ -49,6 +55,11 @@ def syntax(self, **kwargs) -> str: args += ' '.join([arg.syntax() for arg in self._arguments]) elif self._type == ArgumentType.ONEOF: args += f' | '.join([arg.syntax() for arg in self._arguments]) + elif self._type == ArgumentType.FUNCTION: + # Functions should display their token/name, not expand nested arguments + args += self._display + if show_types: + args += f':{self._type.value}' elif self._type != ArgumentType.PURE_TOKEN: args += self._display if show_types: diff --git a/content/commands/ft.hybrid.md b/content/commands/ft.hybrid.md new file mode 100644 index 0000000000..62543e56ab --- /dev/null +++ b/content/commands/ft.hybrid.md @@ -0,0 +1,614 @@ +--- +acl_categories: +- '@read' +- '@search' +arguments: +- name: index + type: string +- arguments: + - name: search_expression + token: SEARCH + type: string + - arguments: + - name: scorer + token: SCORER + type: pure-token + - name: algorithm + type: string + - multiple: true + name: params + type: string + name: scorer + optional: true + type: block + - name: alias_search_score + optional: true + token: YIELD_SCORE_AS + type: string + name: search + type: block +- arguments: + - name: vector_field + token: VSIM + type: string + - name: vector_data + type: string + - arguments: + - arguments: + - name: knn + token: KNN + type: pure-token + - name: count + type: integer + - name: k + token: K + type: integer + - name: ef_runtime + optional: true + token: EF_RUNTIME + type: integer + - name: distance_field + optional: true + token: YIELD_DISTANCE_AS + type: string + name: knn + type: block + - arguments: + - name: range + token: RANGE + type: pure-token + - name: count + type: integer + - name: radius + token: RADIUS + type: double + - name: epsilon + optional: true + token: EPSILON + type: double + - name: distance_field + optional: true + token: YIELD_DISTANCE_AS + type: string + name: range + type: block + name: method + type: oneof + - name: filter_expression + optional: true + token: FILTER + type: string + - arguments: + - name: policy + token: POLICY + type: pure-token + - arguments: + - name: adhoc + token: ADHOC + type: pure-token + - name: batches + token: BATCHES + type: pure-token + - name: acorn + token: ACORN + type: pure-token + name: policy_type + type: oneof + - name: batch_size + optional: true + token: BATCH_SIZE + type: integer + name: policy + optional: true + type: block + name: vsim + type: block +- arguments: + - name: combine + token: COMBINE + type: pure-token + - arguments: + - arguments: + - name: rrf + token: RRF + type: pure-token + - name: count + type: integer + - name: window + optional: true + token: WINDOW + type: integer + - name: constant + optional: true + token: CONSTANT + type: double + name: rrf + type: block + - arguments: + - name: linear + token: LINEAR + type: pure-token + - name: count + type: integer + - name: alpha + optional: true + token: ALPHA + type: double + - name: beta + optional: true + token: BETA + type: double + name: linear + type: block + name: method + type: oneof + - name: alias_combined_score + optional: true + token: YIELD_SCORE_AS + type: string + name: combine + optional: true + type: block +- arguments: + - name: load + token: LOAD + type: pure-token + - name: count + type: integer + - multiple: true + name: field + type: string + name: load + optional: true + type: block +- arguments: + - name: groupby + token: GROUPBY + type: pure-token + - name: count + type: integer + - multiple: true + name: field + type: string + - arguments: + - name: reduce + token: REDUCE + type: pure-token + - name: function + type: string + - name: nargs + type: integer + - multiple: true + name: arg + type: string + multiple: true + name: reduce + type: block + name: groupby + optional: true + type: block +- arguments: + - name: apply + token: APPLY + type: pure-token + - name: expression + type: string + - name: field + token: AS + type: string + multiple: true + name: apply + optional: true + type: block +- arguments: + - name: sortby + token: SORTBY + type: pure-token + - name: field + type: string + - arguments: + - name: asc + token: ASC + type: pure-token + - name: desc + token: DESC + type: pure-token + name: order + optional: true + type: oneof + name: sortby + optional: true + type: block +- name: post_filter_expression + optional: true + token: FILTER + type: string +- arguments: + - name: limit + token: LIMIT + type: pure-token + - name: offset + type: integer + - name: num + type: integer + name: limit + optional: true + type: block +- arguments: + - name: params + token: PARAMS + type: pure-token + - name: count + type: integer + - arguments: + - name: key + type: string + - name: value + type: string + multiple: true + name: values + type: block + name: params + optional: true + type: block +- name: explainscore + optional: true + token: EXPLAINSCORE + type: pure-token +- name: timeout + optional: true + token: TIMEOUT + type: integer +- arguments: + - name: withcursor + token: WITHCURSOR + type: pure-token + - name: read_size + optional: true + token: COUNT + type: integer + - name: idle_time + optional: true + token: MAXIDLE + type: integer + name: withcursor + optional: true + type: block +categories: +- docs +- develop +- stack +- oss +- rs +- rc +- oss +- kubernetes +- clients +complexity: O(N+M) where N is the complexity of the text search and M is the complexity + of the vector search +description: Performs hybrid search combining text search and vector similarity search +group: search +hidden: false +linkTitle: FT.HYBRID +since: 8.4.0 +summary: Performs hybrid search combining text search and vector similarity search +syntax_fmt: "FT.HYBRID index SEARCH query [SCORER\_scorer]\n [YIELD_SCORE_AS\_yield_score_as]\ + \ VSIM field vector [KNN count K\_k\n [EF_RUNTIME\_ef_runtime] [YIELD_SCORE_AS\_\ + yield_score_as] | RANGE\n count RADIUS\_radius [EPSILON\_epsilon]\n [YIELD_SCORE_AS\_\ + yield_score_as]] [FILTER\_filter] [COMBINE ]\n [LIMIT offset num]\ + \ [SORTBY\_sortby [ASC | DESC] | NOSORT] [PARAMS\n nargs name value [name value\ + \ ...]] [TIMEOUT\_timeout]\n [FORMAT\_format] [LOAD\_count field [field ...]] [LOAD\ + \ *] [GROUPBY\n nproperties property [property ...] [REDUCE nargs\n arg [arg ...] [AS\_name] [REDUCE nargs arg [arg ...]\n [AS\_name] ...]]]\ + \ [APPLY\_expression AS\_name [APPLY\_expression\n AS\_name ...]] [FILTER\_filter]" +title: FT.HYBRID +--- + +Performs hybrid search combining text search and vector similarity with configurable fusion methods. + +`FT.HYBRID` provides a unified interface for combining traditional full-text and vector-based search within a single query. It supports hybrid retrieval use cases such as semantic search, Retrieval-Augmented Generation (RAG), and intelligent agent applications. The command builds on the familiar query syntax of `FT.SEARCH` and `FT.AGGREGATE`, simplifying hybrid query construction while enabling flexible post-processing through aggregation capabilities. + +{{< note >}} +This command will only return document IDs (`keyid`) and scores to which the user has read access. +To retrieve entire documents, use projections with `LOAD *` or `LOAD field...`. +{{< /note >}} + +[Examples](#examples) + +## Required arguments + +
+index + +is the name of the index. You must first create the index using [`FT.CREATE`]({{< relref "commands/ft.create/" >}}). +
+ +
+SEARCH "search-expression" + +defines the text search component of the hybrid query. The search expression uses the same syntax as [`FT.SEARCH`]({{< relref "commands/ft.search/" >}}) queries, supporting all text search capabilities including field-specific searches, boolean operations, and phrase matching. +
+ +
+VSIM @vector_field "vector-data" + +defines the vector similarity component of the hybrid query. The `@vector_field` specifies which vector field in the index to search against (for example, `$vector`), and `"vector-data"` contains the query vector for similarity comparison (for example, `PARAMS 2 $vector `). +
+ +## Optional arguments + +
+SCORER algorithm params... + +specifies the scoring algorithm and parameters for the text search component. Supports aliasing and follows the parameter count convention where the first number indicates the total count of following parameters. + +Example: `SCORER 4 BM25 1.2 0.75` uses BM25 algorithm with parameters 1.2 and 0.75. +
+ +
+YIELD_SCORE_AS alias-search-score + +assigns an alias to the search score for use in post-processing operations like `APPLY` or `SORTBY`. +
+ +
+KNN count K topk-k [EF_RUNTIME ef-value] [YIELD_DISTANCE_AS distance_field] + +configures K-nearest neighbors search for vector similarity. The `count` parameter indicates the number of following parameters. `K` specifies the number of nearest neighbors to find. `EF_RUNTIME` controls the search accuracy vs. speed tradeoff. `YIELD_DISTANCE_AS` assigns an alias to the distance value. +
+ +
+RANGE count RADIUS radius-value [EPSILON epsilon-value] [YIELD_DISTANCE_AS distance_field] + +configures range-based vector search within a specified radius. The `count` parameter indicates the number of following parameters. `RADIUS` defines the maximum distance for matches. `EPSILON` provides additional precision control. +
+ +
+FILTER "filter-expression" + +applies pre-filtering to vector search results or post-filtering when used after the `COMBINE` step as post-processing. This filter affects which documents are considered for vector similarity but doesn't impact scoring. In contrast, the `SEARCH` component affects both filtering and scoring. The `FILTER` syntax uses a search expression with the same syntax as [`FT.SEARCH`]({{< relref "/commands/ft.search" >}}), supporting all text search capabilities including field-specific searches, boolean operations, and phrase matching +
+ +
+POLICY [ADHOC_BF|BATCHES] [BATCH_SIZE batch-size-value] + +controls the pre-filtering policy for vector queries. `ADHOC_BF` processes filters on-demand and `BATCHES` processes in configurable batch sizes. See the [pre-filtering policy]({{< relref "/develop/ai/search-and-query/vectors#filters" >}}) for more information. +
+ +
+COMBINE method params... + +specifies how to fuse the text search and vector similarity results. Supports multiple fusion methods: + +- **RRF (Reciprocal Rank Fusion)**: Default method. Parameters include `WINDOW` (default 20) and `CONSTANT` (default 60). +- **LINEAR**: Linear combination with `ALPHA` and `BETA` weights. +- **FUNCTION**: Custom fusion function (future support). + +Example: `COMBINE RRF 4 WINDOW 40 CONSTANT 1.5` +
+ +
+YIELD_SCORE_AS alias-combined-score + +assigns an alias to the combined fusion score for use in post-processing operations. +
+ +
+LOAD count field... + +specifies which fields to return in the results. The `count` parameter indicates the number of fields that follow. + +Example: `LOAD 3 category brand price` +
+ +
+GROUPBY count field... REDUCE function... + +groups results by specified fields and applies reduction functions. Follows the parameter count convention. + +Example: `GROUPBY 4 category brand REDUCE 2 COUNT 0` +
+ +
+APPLY expression AS field + +applies transformations to create new fields. Can reference aliased scores and distances. + +Example: `APPLY "@vector_distance+@score" AS final_score` +
+ +
+SORTBY field [ASC|DESC] + +sorts the final results by the specified field in ascending or descending order. +
+ +
+FILTER post-filter-expression + +applies final filtering to the fused results after combination and before sorting/limiting. +
+ +
+LIMIT offset num + +limits the final results. Default limit is 10 when not specified. The `offset` parameter is zero-indexed. +
+ +
+PARAMS count key value... + +defines parameter substitution for the query. Parameters can be referenced in search expressions using `$parameter_name`. + +Example: `PARAMS 4 min_price 50 max_price 200` +
+ +
+EXPLAINSCORE + +includes detailed score explanations in the results, showing how both text search and vector similarity scores were calculated and combined. +
+ +
+TIMEOUT timeout + +sets a runtime timeout for the query execution in milliseconds. +
+ +
+WITHCURSOR [COUNT read_size] [MAXIDLE idle_time] + +enables cursor-based result pagination for large result sets. `COUNT` specifies the batch size, and `MAXIDLE` sets the cursor timeout. +
+ +## Default values and behaviors + +FT.HYBRID provides sensible defaults to ease onboarding: + +- **Query count**: 2 (one SEARCH and one VSIM component required) +- **Default LIMIT**: 10 results +- **Default SCORER**: BM25STD for text search +- **Default KNN K**: 10 neighbors +- **Default RRF WINDOW**: 20 (or follows LIMIT if specified) +- **Default RRF CONSTANT**: 60 (following Elasticsearch convention) +- **Default EF_RUNTIME**: 10 (as vector KNN [default](https://redis.io/docs/latest/develop/ai/search-and-query/vectors/#hnsw-index)) +- **Default EPSILON**: 0.01 (as the vector RANGE [default]({{< relref "/develop/ai/search-and-query/vectors#hnsw-index" >}})) +## Parameter count convention + +All multi-parameter options use a count prefix that contains ALL tokens that follow: + +- `KNN 4 K 10 EF_RUNTIME 100` - 2 key-value pairs +- `PARAMS 4 min_price 50 max_price 200` - 2 key-value pairs +- `COMBINE RRF 4 WINDOW 40 CONSTANT 1.5` - RRF method with 2 key-value pairs + +The only exception is alias usage with `AS`, which is not counted: +- `APPLY "@vector_distance+@score" AS final_score` +- `LOAD 3 category AS cat brand AS brd price AS prc` + +## Reserved fields + +The following fields are reserved for internal use: + +- `@__key` - reserved for loading key IDs when required +- `@__score` - reserved for the combined score (can be aliased) +- `@vector_distance` - yields the vector distance (can be aliased) +- `@__combined_score` - fused score from the COMBINE step + +## Examples + +
+Basic hybrid search + +Perform a simple hybrid search combining text search for "laptop" with vector similarity: + +{{< highlight bash >}} +127.0.0.1:6379> FT.HYBRID products-idx + SEARCH "laptop" + VSIM @description_vector $query_vec + KNN 2 K 10 +{{< / highlight >}} +
+ +
+Hybrid search with custom scoring and fusion + +Search for electronics with custom BM25 parameters and RRF fusion: + +{{< highlight bash >}} +127.0.0.1:6379> FT.HYBRID products-idx + SEARCH "@category:electronics" + SCORER 4 BM25 1.5 0.8 + YIELD_SCORE_AS text_score + VSIM @features_vector $query_vec + KNN 4 K 20 EF_RUNTIME 200 + YIELD_DISTANCE_AS vector_dist + COMBINE RRF 4 WINDOW 50 CONSTANT 80 + YIELD_SCORE_AS hybrid_score + SORTBY hybrid_score DESC + LIMIT 0 20 +{{< / highlight >}} +
+ +
+Hybrid search with pre-filtering + +Search with vector pre-filtering and post-processing: + +{{< highlight bash >}} +127.0.0.1:6379> FT.HYBRID products-idx + SEARCH "smartphone" + VSIM @image_vector $query_vec + KNN 2 K 15 + FILTER "@price:[100 500]" + COMBINE LINEAR 4 ALPHA 0.7 BETA 0.3 + LOAD 4 title price category rating + APPLY "@price * 0.9" AS discounted_price + SORTBY rating DESC +{{< / highlight >}} +
+ +
+Hybrid search with parameters + +Use parameter substitution for dynamic queries: + +{{< highlight bash >}} +127.0.0.1:6379> FT.HYBRID products-idx + SEARCH "@brand:$brand_name" + VSIM @content_vector $query_vector + RANGE 4 RADIUS 0.8 EPSILON 0.1 + FILTER "@availability:$stock_status" + PARAMS 6 brand_name "Apple" query_vector stock_status "in_stock" + EXPLAINSCORE +{{< / highlight >}} +
+ +## Complexity + +FT.HYBRID complexity depends on both the text search and vector similarity components: +- Text search: O(n) for simple term searches, where n is the number of matching documents. In multi-term queries with INTERSECT or UNION, or when using fuzzy or prefix matches, the complexity increases proportionally to the total number of entries scanned across all participating terms. +- Vector search: O(log n) for KNN with HNSW index, O(n) for range queries +- Fusion: O(k) where k is the number of results to combine +- Overall complexity is typically dominated by the more expensive component + +## Redis Enterprise and Redis Cloud compatibility + +| Redis
Enterprise | Redis
Cloud | Notes | +|:----------------------|:-----------------|:------| +| ❌ Standard
❌ Active-Active | ❌ Standard
❌ Active-Active | | + +## Return information + +{{< multitabs id="ft-hybrid-return-info" + tab1="RESP2" + tab2="RESP3" >}} + +One of the following: +* [Array]({{< relref "/develop/reference/protocol-spec#arrays" >}}) with the first element being the total number of results, followed by document IDs and their field-value pairs as [arrays]({{< relref "/develop/reference/protocol-spec#arrays" >}}). +* [Simple error reply]({{< relref "/develop/reference/protocol-spec#simple-errors" >}}) in these cases: no such index, syntax error in query. + +-tab-sep- + +One of the following: +* [Map]({{< relref "/develop/reference/protocol-spec#maps" >}}) with the following fields: + - `total_results`: [Integer]({{< relref "/develop/reference/protocol-spec#integers" >}}) - total number of results + - `execution_time`: [double]({{< relref "/develop/reference/protocol-spec#doubles" >}}) containing hybrid query execution time + - `warning`: [Array]({{< relref "/develop/reference/protocol-spec#arrays" >}}) of warning messages indicating partial results due to index errors or `MAXPREFIXEXPANSIONS` and `TIMEOUT` reached + - `results`: [Array]({{< relref "/develop/reference/protocol-spec#arrays" >}}) of [maps]({{< relref "/develop/reference/protocol-spec#maps" >}}) containing document information +* [Simple error reply]({{< relref "/develop/reference/protocol-spec#simple-errors" >}}) in these cases: no such index, syntax error in query. + +{{< /multitabs >}} + +## See also + +[`FT.CREATE`]({{< relref "commands/ft.create/" >}}) | [`FT.SEARCH`]({{< relref "commands/ft.search/" >}}) | [`FT.AGGREGATE`]({{< relref "commands/ft.aggregate/" >}}) + +## Related topics + +- [Vector search concepts]({{< relref "/develop/ai/search-and-query/vectors" >}}) +- [Combined search]({{< relref "/develop/ai/search-and-query/query/combined/" >}})